From 69de0304759a7eebf28fc752ffba9bf64156ea75 Mon Sep 17 00:00:00 2001 From: Aleksander Morgado Date: Fri, 19 Oct 2018 15:55:21 +0200 Subject: broadband-modem: enable and handle +CGEV indications The +CGEV indications allow us to get notified of packet domain events like network-initiated or ME-initiated disconnections on specific connected contexts, as well as full PS detach events (all contexts disconnected). If the modem supports these indications, we will enable them with +CGEREP and will then process them when they are emitted by the modem. If a full PS detach event happens, we will explicitly disconnect all connected bearers. If a deactivation inidication is received for a single context, we will look for the associated bearer by CID and explicitly disconnect it. --- src/mm-broadband-modem.c | 548 +++++++++++++++++++++++++++++++++-------- src/mm-modem-helpers.c | 315 +++++++++++++++++++++++ src/mm-modem-helpers.h | 42 ++++ src/tests/test-modem-helpers.c | 157 ++++++++++++ 4 files changed, 953 insertions(+), 109 deletions(-) diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c index 7ee68d37a..7c7250e69 100644 --- a/src/mm-broadband-modem.c +++ b/src/mm-broadband-modem.c @@ -157,6 +157,8 @@ struct _MMBroadbandModemPrivate { MM3gppCmerMode modem_cmer_enable_mode; MM3gppCmerMode modem_cmer_disable_mode; MM3gppCmerInd modem_cmer_ind; + gboolean modem_cgerep_support_checked; + gboolean modem_cgerep_supported; MMFlowControl flow_control; /*<--- Modem 3GPP interface --->*/ @@ -2662,6 +2664,269 @@ modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self, return g_task_propagate_boolean (G_TASK (res), error); } +static void +bearer_report_disconnected (MMBaseBearer *bearer, + gpointer user_data) +{ + guint cid; + + cid = GPOINTER_TO_UINT (user_data); + + /* If we're told to disconnect a single context and this is not the + * bearer associated to that context, ignore operation */ + if (cid > 0 && + MM_IS_BROADBAND_BEARER (bearer) && + mm_broadband_bearer_get_3gpp_cid (MM_BROADBAND_BEARER (bearer)) != cid) + return; + + /* If already disconnected, ignore operation */ + if (mm_base_bearer_get_status (bearer) == MM_BEARER_STATUS_DISCONNECTED) + return; + + mm_info ("Bearer %s: explicitly disconnected", mm_base_bearer_get_path (bearer)); + mm_base_bearer_report_connection_status (bearer, MM_BEARER_CONNECTION_STATUS_DISCONNECTED); +} + +static void +bearer_list_report_disconnections (MMBroadbandModem *self, + guint cid) +{ + MMBearerList *list = NULL; + + g_object_get (self, + MM_IFACE_MODEM_BEARER_LIST, &list, + NULL); + + /* If empty bearer list, nothing else to do */ + if (!list) + return; + + mm_bearer_list_foreach (list, (MMBearerListForeachFunc)bearer_report_disconnected, GUINT_TO_POINTER (cid)); + g_object_unref (list); +} + +static void +cgev_process_detach (MMBroadbandModem *self, + MM3gppCgev type) +{ + switch (type) { + case MM_3GPP_CGEV_NW_DETACH: + mm_info ("network forced PS detach: all contexts have been deactivated"); + bearer_list_report_disconnections (self, 0); + break; + case MM_3GPP_CGEV_ME_DETACH: + mm_info ("mobile equipment forced PS detach: all contexts have been deactivated"); + bearer_list_report_disconnections (self, 0); + break; + default: + g_assert_not_reached (); + } +} + +static void +cgev_process_primary (MMBroadbandModem *self, + MM3gppCgev type, + const gchar *str) +{ + GError *error = NULL; + guint cid = 0; + + if (!mm_3gpp_parse_cgev_indication_primary (str, type, &cid, &error)) { + mm_warn ("couldn't parse cid info from +CGEV indication '%s': %s", str, error->message); + g_error_free (error); + return; + } + + switch (type) { + case MM_3GPP_CGEV_NW_ACT_PRIMARY: + mm_info ("network request to activate context (cid %u)", cid); + break; + case MM_3GPP_CGEV_ME_ACT_PRIMARY: + mm_info ("mobile equipment request to activate context (cid %u)", cid); + break; + case MM_3GPP_CGEV_NW_DEACT_PRIMARY: + mm_info ("network request to deactivate context (cid %u)", cid); + bearer_list_report_disconnections (self, cid); + break; + case MM_3GPP_CGEV_ME_DEACT_PRIMARY: + mm_info ("mobile equipment request to deactivate context (cid %u)", cid); + bearer_list_report_disconnections (self, cid); + break; + default: + g_assert_not_reached (); + break; + } +} + +static void +cgev_process_secondary (MMBroadbandModem *self, + MM3gppCgev type, + const gchar *str) +{ + GError *error = NULL; + guint p_cid = 0; + guint cid = 0; + + if (!mm_3gpp_parse_cgev_indication_secondary (str, type, &p_cid, &cid, NULL, &error)) { + mm_warn ("couldn't parse p_cid/cid info from +CGEV indication '%s': %s", str, error->message); + g_error_free (error); + return; + } + + switch (type) { + case MM_3GPP_CGEV_NW_ACT_SECONDARY: + mm_info ("network request to activate secondary context (cid %u, primary cid %u)", cid, p_cid); + break; + case MM_3GPP_CGEV_ME_ACT_SECONDARY: + mm_info ("mobile equipment request to activate secondary context (cid %u, primary cid %u)", cid, p_cid); + break; + case MM_3GPP_CGEV_NW_DEACT_SECONDARY: + mm_info ("network request to deactivate secondary context (cid %u, primary cid %u)", cid, p_cid); + bearer_list_report_disconnections (self, cid); + break; + case MM_3GPP_CGEV_ME_DEACT_SECONDARY: + mm_info ("mobile equipment request to deactivate secondary context (cid %u, primary cid %u)", cid, p_cid); + bearer_list_report_disconnections (self, cid); + break; + default: + g_assert_not_reached (); + break; + } +} + +static void +cgev_process_pdp (MMBroadbandModem *self, + MM3gppCgev type, + const gchar *str) +{ + GError *error = NULL; + gchar *pdp_type = NULL; + gchar *pdp_addr = NULL; + guint cid = 0; + + if (!mm_3gpp_parse_cgev_indication_pdp (str, type, &pdp_type, &pdp_addr, &cid, &error)) { + mm_warn ("couldn't parse PDP info from +CGEV indication '%s': %s", str, error->message); + g_error_free (error); + return; + } + + switch (type) { + case MM_3GPP_CGEV_REJECT: + mm_info ("network request to activate context (type %s, address %s) has been automatically rejected", pdp_type, pdp_addr); + break; + case MM_3GPP_CGEV_NW_REACT: + /* NOTE: we don't currently notify about automatic reconnections like this one */ + if (cid) + mm_info ("network request to reactivate context (type %s, address %s, cid %u)", pdp_type, pdp_addr, cid); + else + mm_info ("network request to reactivate context (type %s, address %s, cid unknown)", pdp_type, pdp_addr); + break; + case MM_3GPP_CGEV_NW_DEACT_PDP: + if (cid) { + mm_info ("network request to deactivate context (type %s, address %s, cid %u)", pdp_type, pdp_addr, cid); + bearer_list_report_disconnections (self, cid); + } else + mm_info ("network request to deactivate context (type %s, address %s, cid unknown)", pdp_type, pdp_addr); + break; + case MM_3GPP_CGEV_ME_DEACT_PDP: + if (cid) { + mm_info ("mobile equipment request to deactivate context (type %s, address %s, cid %u)", pdp_type, pdp_addr, cid); + bearer_list_report_disconnections (self, cid); + } else + mm_info ("mobile equipment request to deactivate context (type %s, address %s, cid unknown)", pdp_type, pdp_addr); + break; + default: + g_assert_not_reached (); + break; + } + + g_free (pdp_addr); + g_free (pdp_type); +} + +static void +cgev_received (MMPortSerialAt *port, + GMatchInfo *info, + MMBroadbandModem *self) +{ + gchar *str; + MM3gppCgev type; + + str = mm_get_string_unquoted_from_match_info (info, 1); + if (!str) + return; + + type = mm_3gpp_parse_cgev_indication_action (str); + + switch (type) { + case MM_3GPP_CGEV_NW_DETACH: + case MM_3GPP_CGEV_ME_DETACH: + cgev_process_detach (self, type); + break; + case MM_3GPP_CGEV_NW_ACT_PRIMARY: + case MM_3GPP_CGEV_ME_ACT_PRIMARY: + case MM_3GPP_CGEV_NW_DEACT_PRIMARY: + case MM_3GPP_CGEV_ME_DEACT_PRIMARY: + cgev_process_primary (self, type, str); + break; + case MM_3GPP_CGEV_NW_ACT_SECONDARY: + case MM_3GPP_CGEV_ME_ACT_SECONDARY: + case MM_3GPP_CGEV_NW_DEACT_SECONDARY: + case MM_3GPP_CGEV_ME_DEACT_SECONDARY: + cgev_process_secondary (self, type, str); + break; + case MM_3GPP_CGEV_NW_DEACT_PDP: + case MM_3GPP_CGEV_ME_DEACT_PDP: + case MM_3GPP_CGEV_REJECT: + case MM_3GPP_CGEV_NW_REACT: + cgev_process_pdp (self, type, str); + break; + case MM_3GPP_CGEV_NW_CLASS: + case MM_3GPP_CGEV_ME_CLASS: + case MM_3GPP_CGEV_NW_MODIFY: + case MM_3GPP_CGEV_ME_MODIFY: + /* ignore */ + break; + default: + mm_dbg ("unhandled +CGEV indication: %s", str); + break; + } + + g_free (str); +} + +static void +set_cgev_unsolicited_events_handlers (MMBroadbandModem *self, + gboolean enable) +{ + MMPortSerialAt *ports[2]; + GRegex *cgev_regex; + guint i; + + cgev_regex = mm_3gpp_cgev_regex_get (); + ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); + ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); + + /* Enable unsolicited events in given port */ + for (i = 0; i < 2; i++) { + if (!ports[i]) + continue; + + /* Set/unset unsolicited CGEV event handler */ + mm_dbg ("(%s) %s 3GPP +CGEV unsolicited events handlers", + mm_port_get_device (MM_PORT (ports[i])), + enable ? "Setting" : "Removing"); + mm_port_serial_at_add_unsolicited_msg_handler ( + ports[i], + cgev_regex, + enable ? (MMPortSerialAtUnsolicitedMsgFn) cgev_received : NULL, + enable ? self : NULL, + NULL); + } + + g_regex_unref (cgev_regex); +} + static void ciev_received (MMPortSerialAt *port, GMatchInfo *info, @@ -2701,8 +2966,8 @@ ciev_received (MMPortSerialAt *port, } static void -set_unsolicited_events_handlers (MMBroadbandModem *self, - gboolean enable) +set_ciev_unsolicited_events_handlers (MMBroadbandModem *self, + gboolean enable) { MMPortSerialAt *ports[2]; GRegex *ciev_regex; @@ -2718,7 +2983,7 @@ set_unsolicited_events_handlers (MMBroadbandModem *self, continue; /* Set/unset unsolicited CIEV event handler */ - mm_dbg ("(%s) %s 3GPP unsolicited events handlers", + mm_dbg ("(%s) %s 3GPP +CIEV unsolicited events handlers", mm_port_get_device (MM_PORT (ports[i])), enable ? "Setting" : "Removing"); mm_port_serial_at_add_unsolicited_msg_handler ( @@ -2733,9 +2998,51 @@ set_unsolicited_events_handlers (MMBroadbandModem *self, } static void -cmer_format_check_ready (MMBroadbandModem *self, - GAsyncResult *res, - GTask *task) +support_checked_setup_unsolicited_events (GTask *task) +{ + MMBroadbandModem *self; + + self = g_task_get_source_object (task); + + if (self->priv->modem_cind_supported) + set_ciev_unsolicited_events_handlers (self, TRUE); + + if (self->priv->modem_cgerep_supported) + set_cgev_unsolicited_events_handlers (self, TRUE); + + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +static void check_and_setup_3gpp_urc_support (GTask *task); + +static void +cgerep_format_check_ready (MMBroadbandModem *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + const gchar *result; + + result = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); + if (!result) { + mm_dbg ("+CGEREP check failed, marking packet domain event reporting as unsupported: '%s'", error->message); + g_error_free (error); + goto out; + } + + mm_dbg ("Modem supports packet domain event reporting"); + self->priv->modem_cgerep_supported = TRUE; + +out: + /* go on with remaining checks */ + check_and_setup_3gpp_urc_support (task); +} + +static void +cmer_format_check_ready (MMBroadbandModem *self, + GAsyncResult *res, + GTask *task) { MM3gppCmerMode supported_modes = MM_3GPP_CMER_MODE_NONE; MM3gppCmerInd supported_inds = MM_3GPP_CMER_IND_NONE; @@ -2747,9 +3054,7 @@ cmer_format_check_ready (MMBroadbandModem *self, if (error || !mm_3gpp_parse_cmer_test_response (result, &supported_modes, &supported_inds, &error)) { mm_dbg ("+CMER check failed, marking indications as unsupported: '%s'", error->message); g_error_free (error); - g_task_return_boolean (task, TRUE); - g_object_unref (task); - return; + goto out; } aux = mm_3gpp_cmer_mode_build_string_from_mask (supported_modes); @@ -2789,17 +3094,15 @@ cmer_format_check_ready (MMBroadbandModem *self, mm_dbg ("+CMER indication setting: %s", aux); g_free (aux); - /* Now, keep on setting up the ports */ - set_unsolicited_events_handlers (self, TRUE); - - g_task_return_boolean (task, TRUE); - g_object_unref (task); +out: + /* go on with remaining checks */ + check_and_setup_3gpp_urc_support (task); } static void cind_format_check_ready (MMBroadbandModem *self, - GAsyncResult *res, - GTask *task) + GAsyncResult *res, + GTask *task) { GHashTable *indicators = NULL; GError *error = NULL; @@ -2812,8 +3115,8 @@ cind_format_check_ready (MMBroadbandModem *self, /* unsupported indications */ mm_dbg ("+CIND check failed, marking indications as unsupported: '%s'", error->message); g_error_free (error); - g_task_return_boolean (task, TRUE); - g_object_unref (task); + /* go on with remaining checks */ + check_and_setup_3gpp_urc_support (task); return; } @@ -2866,16 +3169,13 @@ cind_format_check_ready (MMBroadbandModem *self, } static void -modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *_self, - GAsyncReadyCallback callback, - gpointer user_data) +check_and_setup_3gpp_urc_support (GTask *task) { - MMBroadbandModem *self = MM_BROADBAND_MODEM (_self); - GTask *task; + MMBroadbandModem *self; - task = g_task_new (self, NULL, callback, user_data); + self = g_task_get_source_object (task); - /* Load supported indicators */ + /* Check support for +CIEV indications, managed with +CIND/+CMER */ if (!self->priv->modem_cind_support_checked) { mm_dbg ("Checking indicator support..."); self->priv->modem_cind_support_checked = TRUE; @@ -2888,27 +3188,48 @@ modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *_self, return; } - /* If supported, go on */ - if (self->priv->modem_cind_supported) - set_unsolicited_events_handlers (self, TRUE); + /* Check support for +CGEV indications, managed with +CGEREP */ + if (!self->priv->modem_cgerep_support_checked) { + mm_dbg ("Checking packet domain event reporting..."); + self->priv->modem_cgerep_support_checked = TRUE; + mm_base_modem_at_command (MM_BASE_MODEM (self), + "+CGEREP=?", + 3, + TRUE, + (GAsyncReadyCallback)cgerep_format_check_ready, + task); + return; + } - g_task_return_boolean (task, TRUE); - g_object_unref (task); + support_checked_setup_unsolicited_events (task); } static void -modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *_self, - GAsyncReadyCallback callback, - gpointer user_data) +modem_3gpp_setup_unsolicited_events (MMIfaceModem3gpp *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + task = g_task_new (self, NULL, callback, user_data); + check_and_setup_3gpp_urc_support (task); +} + +static void +modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *_self, + GAsyncReadyCallback callback, + gpointer user_data) { MMBroadbandModem *self = MM_BROADBAND_MODEM (_self); GTask *task; task = g_task_new (self, NULL, callback, user_data); - /* If supported, go on */ if (self->priv->modem_cind_support_checked && self->priv->modem_cind_supported) - set_unsolicited_events_handlers (self, FALSE); + set_ciev_unsolicited_events_handlers (self, FALSE); + + if (self->priv->modem_cgerep_supported) + set_cgev_unsolicited_events_handlers (self, FALSE); g_task_return_boolean (task, TRUE); g_object_unref (task); @@ -2918,16 +3239,26 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *_self, /* Enabling/disabling unsolicited events (3GPP interface) */ typedef struct { - gchar *command; - gboolean enable; - gboolean cmer_primary_done; - gboolean cmer_secondary_done; + gboolean enable; + MMPortSerialAt *primary; + MMPortSerialAt *secondary; + gchar *cmer_command; + gboolean cmer_primary_done; + gboolean cmer_secondary_done; + gchar *cgerep_command; + gboolean cgerep_primary_done; + gboolean cgerep_secondary_done; } UnsolicitedEventsContext; static void unsolicited_events_context_free (UnsolicitedEventsContext *ctx) { - g_free (ctx->command); + if (ctx->secondary) + g_object_unref (ctx->secondary); + if (ctx->primary) + g_object_unref (ctx->primary); + g_free (ctx->cgerep_command); + g_free (ctx->cmer_command); g_free (ctx); } @@ -2943,53 +3274,70 @@ static void run_unsolicited_events_setup (GTask *task); static void unsolicited_events_setup_ready (MMBroadbandModem *self, - GAsyncResult *res, - GTask *task) + GAsyncResult *res, + GTask *task) { UnsolicitedEventsContext *ctx; - GError *error = NULL; - - mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error); - if (!error) { - /* Run on next port, if any */ - run_unsolicited_events_setup (task); - return; - } + GError *error = NULL; ctx = g_task_get_task_data (task); - mm_dbg ("Couldn't %s event reporting: '%s'", - ctx->enable ? "enable" : "disable", - error->message); - g_error_free (error); - /* Consider this operation complete, ignoring errors */ - g_task_return_boolean (task, TRUE); - g_object_unref (task); + if (!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error)) { + mm_dbg ("Couldn't %s event reporting: '%s'", + ctx->enable ? "enable" : "disable", + error->message); + g_error_free (error); + } + + /* Continue on next port/command */ + run_unsolicited_events_setup (task); } static void run_unsolicited_events_setup (GTask *task) { - MMBroadbandModem *self; + MMBroadbandModem *self; UnsolicitedEventsContext *ctx; - MMPortSerialAt *port = NULL; + MMPortSerialAt *port = NULL; + const gchar *command = NULL; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); - if (!ctx->cmer_primary_done) { + /* CMER on primary port */ + if (!ctx->cmer_primary_done && ctx->cmer_command && ctx->primary) { + mm_dbg ("Enabling +CIND event reporting in primary port..."); ctx->cmer_primary_done = TRUE; - port = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); - } else if (!ctx->cmer_secondary_done) { + command = ctx->cmer_command; + port = ctx->primary; + } + /* CMER on secondary port */ + else if (!ctx->cmer_secondary_done && ctx->cmer_command && ctx->secondary) { + mm_dbg ("Enabling +CIND event reporting in secondary port..."); ctx->cmer_secondary_done = TRUE; - port = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self)); + command = ctx->cmer_command; + port = ctx->secondary; + } + /* CGEREP on primary port */ + else if (!ctx->cgerep_primary_done && ctx->cgerep_command && ctx->primary) { + mm_dbg ("Enabling +CGEV event reporting in primary port..."); + ctx->cgerep_primary_done = TRUE; + command = ctx->cgerep_command; + port = ctx->primary; + } + /* CGEREP on secondary port */ + else if (!ctx->cgerep_secondary_done && ctx->cgerep_command && ctx->secondary) { + mm_dbg ("Enabling +CGEV event reporting in secondary port..."); + ctx->cgerep_secondary_done = TRUE; + port = ctx->secondary; + command = ctx->cgerep_command; } /* Enable unsolicited events in given port */ - if (port) { + if (port && command) { mm_base_modem_at_command_full (MM_BASE_MODEM (self), port, - ctx->command, + command, 3, FALSE, FALSE, /* raw */ @@ -2999,7 +3347,7 @@ run_unsolicited_events_setup (GTask *task) return; } - /* If no more ports, we're fully done now */ + /* Fully done now */ g_task_return_boolean (task, TRUE); g_object_unref (task); } @@ -3009,34 +3357,25 @@ modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { - MMBroadbandModem *self = MM_BROADBAND_MODEM (_self); - GTask *task; + MMBroadbandModem *self = MM_BROADBAND_MODEM (_self); + GTask *task; + UnsolicitedEventsContext *ctx; task = g_task_new (self, NULL, callback, user_data); - /* If supported, go on */ - if (self->priv->modem_cind_support_checked && self->priv->modem_cind_supported) { - gchar *cmd; - - /* If CMER command available, launch it */ - cmd = mm_3gpp_build_cmer_set_request (self->priv->modem_cmer_enable_mode, self->priv->modem_cmer_ind); - if (cmd) { - UnsolicitedEventsContext *ctx; - - ctx = g_new0 (UnsolicitedEventsContext, 1); - ctx->enable = TRUE; - ctx->command = cmd; + ctx = g_new0 (UnsolicitedEventsContext, 1); + ctx->enable = TRUE; + ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self)); + ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self)); + g_task_set_task_data (task, ctx, (GDestroyNotify)unsolicited_events_context_free); - g_task_set_task_data (task, ctx, (GDestroyNotify)unsolicited_events_context_free); + if (self->priv->modem_cind_support_checked && self->priv->modem_cind_supported) + ctx->cmer_command = mm_3gpp_build_cmer_set_request (self->priv->modem_cmer_enable_mode, self->priv->modem_cmer_ind); - run_unsolicited_events_setup (task); - return; - } - mm_dbg ("Skipping +CMER enable command: not supported"); - } + if (self->priv->modem_cgerep_support_checked && self->priv->modem_cgerep_supported) + ctx->cgerep_command = g_strdup ("+CGEREP=2"); - g_task_return_boolean (task, TRUE); - g_object_unref (task); + run_unsolicited_events_setup (task); } static void @@ -3044,33 +3383,24 @@ modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *_self, GAsyncReadyCallback callback, gpointer user_data) { - MMBroadbandModem *self = MM_BROADBAND_MODEM (_self); - GTask *task; + MMBroadbandModem *self = MM_BROADBAND_MODEM (_self); + GTask *task; + UnsolicitedEventsContext *ctx; task = g_task_new (self, NULL, callback, user_data); - /* If CIND supported, go on */ - if (self->priv->modem_cind_support_checked && self->priv->modem_cind_supported) { - gchar *cmd; - - /* If CMER command available, launch it */ - cmd = mm_3gpp_build_cmer_set_request (self->priv->modem_cmer_disable_mode, MM_3GPP_CMER_IND_NONE); - if (cmd) { - UnsolicitedEventsContext *ctx; - - ctx = g_new0 (UnsolicitedEventsContext, 1); - ctx->command = cmd; + ctx = g_new0 (UnsolicitedEventsContext, 1); + ctx->primary = mm_base_modem_get_port_primary (MM_BASE_MODEM (self)); + ctx->secondary = mm_base_modem_get_port_secondary (MM_BASE_MODEM (self)); + g_task_set_task_data (task, ctx, (GDestroyNotify)unsolicited_events_context_free); - g_task_set_task_data (task, ctx, (GDestroyNotify)unsolicited_events_context_free); + if (self->priv->modem_cind_support_checked && self->priv->modem_cind_supported) + ctx->cmer_command = mm_3gpp_build_cmer_set_request (self->priv->modem_cmer_disable_mode, MM_3GPP_CMER_IND_NONE); - run_unsolicited_events_setup (task); - return; - } - mm_dbg ("Skipping +CMER disable command: not supported"); - } + if (self->priv->modem_cgerep_support_checked && self->priv->modem_cgerep_supported) + ctx->cgerep_command = g_strdup ("+CGEREP=0"); - g_task_return_boolean (task, TRUE); - g_object_unref (task); + run_unsolicited_events_setup (task); } /*****************************************************************************/ diff --git a/src/mm-modem-helpers.c b/src/mm-modem-helpers.c index 1da47a987..c999637ae 100644 --- a/src/mm-modem-helpers.c +++ b/src/mm-modem-helpers.c @@ -864,6 +864,17 @@ mm_3gpp_ciev_regex_get (void) /*************************************************************************/ +GRegex * +mm_3gpp_cgev_regex_get (void) +{ + return g_regex_new ("\\r\\n\\+CGEV:\\s*(.*)\\r\\n", + G_REGEX_RAW | G_REGEX_OPTIMIZE, + 0, + NULL); +} + +/*************************************************************************/ + GRegex * mm_3gpp_cusd_regex_get (void) { @@ -3264,6 +3275,310 @@ done: return array; } +/*************************************************************************/ +/* +CGEV indication parser + * + * We provide full parsing support, including parameters, for these messages: + * +CGEV: NW DETACH + * +CGEV: ME DETACH + * +CGEV: NW PDN ACT + * +CGEV: ME PDN ACT [,[,]] + * +CGEV: NW ACT , , + * +CGEV: ME ACT , , + * +CGEV: NW DEACT , , [] + * +CGEV: ME DEACT , , [] + * +CGEV: NW PDN DEACT + * +CGEV: ME PDN DEACT + * +CGEV: NW DEACT , , + * +CGEV: ME DEACT , , + * +CGEV: REJECT , + * +CGEV: NW REACT , , [] + * + * We don't provide parameter parsing for these messages: + * +CGEV: NW CLASS + * +CGEV: ME CLASS + * +CGEV: NW MODIFY , , + * +CGEV: ME MODIFY , , + */ + +static gboolean +deact_secondary (const gchar *str) +{ + /* We need to detect the ME/NW DEACT format. + * Either, + * +CGEV: NW DEACT , , [] + * +CGEV: ME DEACT , , [] + * or, + * +CGEV: NW DEACT , , + * +CGEV: ME DEACT , , + */ + str = strstr (str, "DEACT") + 5; + while (*str == ' ') + str++; + + /* We will look for because we know it's NUMERIC */ + return g_ascii_isdigit (*str); +} + +MM3gppCgev +mm_3gpp_parse_cgev_indication_action (const gchar *str) +{ + str = mm_strip_tag (str, "+CGEV:"); + if (g_str_has_prefix (str, "NW DETACH")) + return MM_3GPP_CGEV_NW_DETACH; + if (g_str_has_prefix (str, "ME DETACH")) + return MM_3GPP_CGEV_ME_DETACH; + if (g_str_has_prefix (str, "NW CLASS")) + return MM_3GPP_CGEV_NW_CLASS; + if (g_str_has_prefix (str, "ME CLASS")) + return MM_3GPP_CGEV_ME_CLASS; + if (g_str_has_prefix (str, "NW PDN ACT")) + return MM_3GPP_CGEV_NW_ACT_PRIMARY; + if (g_str_has_prefix (str, "ME PDN ACT")) + return MM_3GPP_CGEV_ME_ACT_PRIMARY; + if (g_str_has_prefix (str, "NW ACT")) + return MM_3GPP_CGEV_NW_ACT_SECONDARY; + if (g_str_has_prefix (str, "ME ACT")) + return MM_3GPP_CGEV_ME_ACT_SECONDARY; + if (g_str_has_prefix (str, "NW DEACT")) + return (deact_secondary (str) ? MM_3GPP_CGEV_NW_DEACT_SECONDARY : MM_3GPP_CGEV_NW_DEACT_PDP); + if (g_str_has_prefix (str, "ME DEACT")) + return (deact_secondary (str) ? MM_3GPP_CGEV_ME_DEACT_SECONDARY : MM_3GPP_CGEV_ME_DEACT_PDP); + if (g_str_has_prefix (str, "NW PDN DEACT")) + return MM_3GPP_CGEV_NW_DEACT_PRIMARY; + if (g_str_has_prefix (str, "ME PDN DEACT")) + return MM_3GPP_CGEV_ME_DEACT_PRIMARY; + if (g_str_has_prefix (str, "NW MODIFY")) + return MM_3GPP_CGEV_NW_MODIFY; + if (g_str_has_prefix (str, "ME MODIFY")) + return MM_3GPP_CGEV_ME_MODIFY; + if (g_str_has_prefix (str, "NW REACT")) + return MM_3GPP_CGEV_NW_REACT; + if (g_str_has_prefix (str, "REJECT")) + return MM_3GPP_CGEV_REJECT; + return MM_3GPP_CGEV_UNKNOWN; +} + +/* + * +CGEV: NW DEACT , , [] + * +CGEV: ME DEACT , , [] + * +CGEV: REJECT , + * +CGEV: NW REACT , , [] + */ +gboolean +mm_3gpp_parse_cgev_indication_pdp (const gchar *str, + MM3gppCgev type, + gchar **out_pdp_type, + gchar **out_pdp_addr, + guint *out_cid, + GError **error) +{ + GRegex *r; + GMatchInfo *match_info = NULL; + GError *inner_error = NULL; + gchar *pdp_type = NULL; + gchar *pdp_addr = NULL; + guint cid = 0; + + g_assert (type == MM_3GPP_CGEV_REJECT || + type == MM_3GPP_CGEV_NW_REACT || + type == MM_3GPP_CGEV_NW_DEACT_PDP || + type == MM_3GPP_CGEV_ME_DEACT_PDP); + + r = g_regex_new ("(?:" + "REJECT|" + "NW REACT|" + "NW DEACT|ME DEACT" + ")\\s*([^,]*),\\s*([^,]*)(?:,\\s*([0-9]+))?", 0, 0, NULL); + + str = mm_strip_tag (str, "+CGEV:"); + g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error); + if (inner_error) + goto out; + + if (!g_match_info_matches (match_info)) { + inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match response"); + goto out; + } + + if (out_pdp_type && !(pdp_type = mm_get_string_unquoted_from_match_info (match_info, 1))) { + inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing PDP type"); + goto out; + } + + if (out_pdp_addr && !(pdp_addr = mm_get_string_unquoted_from_match_info (match_info, 2))) { + inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing PDP addr"); + goto out; + } + + /* CID is optional */ + if (out_cid && + (g_match_info_get_match_count (match_info) >= 4) && + !mm_get_uint_from_match_info (match_info, 3, &cid)) { + inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing CID"); + goto out; + } + +out: + if (match_info) + g_match_info_free (match_info); + g_regex_unref (r); + + if (inner_error) { + g_free (pdp_type); + g_free (pdp_addr); + g_propagate_error (error, inner_error); + return FALSE; + } + + if (out_pdp_type) + *out_pdp_type = pdp_type; + if (out_pdp_addr) + *out_pdp_addr = pdp_addr; + if (out_cid) + *out_cid = cid; + return TRUE; +} + +/* + * +CGEV: NW PDN ACT + * +CGEV: ME PDN ACT [,[,]] + * +CGEV: NW PDN DEACT + * +CGEV: ME PDN DEACT + * + * NOTE: the special case of a "ME PDN ACT" notification with the additional + * and fields is telling us that was NOT connected + * but was connected instead, which may happen when trying to + * connect a IPv4v6 context but the modem ended up connecting a IPv4-only or + * IPv6-only context instead. We are right now ignoring this, and assuming the + * that we requested is the one reported as connected. + */ +gboolean +mm_3gpp_parse_cgev_indication_primary (const gchar *str, + MM3gppCgev type, + guint *out_cid, + GError **error) +{ + GRegex *r; + GMatchInfo *match_info = NULL; + GError *inner_error = NULL; + guint cid = 0; + + g_assert ((type == MM_3GPP_CGEV_NW_ACT_PRIMARY) || + (type == MM_3GPP_CGEV_ME_ACT_PRIMARY) || + (type == MM_3GPP_CGEV_NW_DEACT_PRIMARY) || + (type == MM_3GPP_CGEV_ME_DEACT_PRIMARY)); + + r = g_regex_new ("(?:" + "NW PDN ACT|ME PDN ACT|" + "NW PDN DEACT|ME PDN DEACT|" + ")\\s*([0-9]+)", 0, 0, NULL); + + str = mm_strip_tag (str, "+CGEV:"); + g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error); + if (inner_error) + goto out; + + if (!g_match_info_matches (match_info)) { + inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match response"); + goto out; + } + + if (out_cid && !mm_get_uint_from_match_info (match_info, 1, &cid)) { + inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing CID"); + goto out; + } + +out: + if (match_info) + g_match_info_free (match_info); + g_regex_unref (r); + + if (inner_error) { + g_propagate_error (error, inner_error); + return FALSE; + } + + if (out_cid) + *out_cid = cid; + return TRUE; +} + +/* + * +CGEV: NW ACT , , + * +CGEV: ME ACT , , + * +CGEV: NW DEACT , , + * +CGEV: ME DEACT , , + */ +gboolean +mm_3gpp_parse_cgev_indication_secondary (const gchar *str, + MM3gppCgev type, + guint *out_p_cid, + guint *out_cid, + guint *out_event_type, + GError **error) +{ + GRegex *r; + GMatchInfo *match_info = NULL; + GError *inner_error = NULL; + guint p_cid = 0; + guint cid = 0; + guint event_type = 0; + + g_assert (type == MM_3GPP_CGEV_NW_ACT_SECONDARY || + type == MM_3GPP_CGEV_ME_ACT_SECONDARY || + type == MM_3GPP_CGEV_NW_DEACT_SECONDARY || + type == MM_3GPP_CGEV_ME_DEACT_SECONDARY); + + r = g_regex_new ("(?:" + "NW ACT|ME ACT|" + "NW DEACT|ME DEACT" + ")\\s*([0-9]+),\\s*([0-9]+),\\s*([0-9]+)", 0, 0, NULL); + + str = mm_strip_tag (str, "+CGEV:"); + g_regex_match_full (r, str, strlen (str), 0, 0, &match_info, &inner_error); + if (inner_error) + goto out; + + if (!g_match_info_matches (match_info)) { + inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Couldn't match response"); + goto out; + } + + if (out_p_cid && !mm_get_uint_from_match_info (match_info, 1, &p_cid)) { + inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing primary CID"); + goto out; + } + + if (out_cid && !mm_get_uint_from_match_info (match_info, 2, &cid)) { + inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing secondary CID"); + goto out; + } + + if (out_event_type && !mm_get_uint_from_match_info (match_info, 3, &event_type)) { + inner_error = g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_FAILED, "Error parsing event type"); + goto out; + } + +out: + if (match_info) + g_match_info_free (match_info); + g_regex_unref (r); + + if (inner_error) { + g_propagate_error (error, inner_error); + return FALSE; + } + + if (out_p_cid) + *out_p_cid = p_cid; + if (out_cid) + *out_cid = cid; + if (out_event_type) + *out_event_type = event_type; + return TRUE; +} + /*************************************************************************/ void diff --git a/src/mm-modem-helpers.h b/src/mm-modem-helpers.h index 71d8893ea..237046ad7 100644 --- a/src/mm-modem-helpers.h +++ b/src/mm-modem-helpers.h @@ -125,6 +125,7 @@ MMFlowControl mm_flow_control_from_string (const gchar *str, GPtrArray *mm_3gpp_creg_regex_get (gboolean solicited); void mm_3gpp_creg_regex_destroy (GPtrArray *array); GRegex *mm_3gpp_ciev_regex_get (void); +GRegex *mm_3gpp_cgev_regex_get (void); GRegex *mm_3gpp_cusd_regex_get (void); GRegex *mm_3gpp_cmti_regex_get (void); GRegex *mm_3gpp_cds_regex_get (void); @@ -272,6 +273,47 @@ gint mm_3gpp_cind_response_get_max (MM3gppCindResponse *r); GByteArray *mm_3gpp_parse_cind_read_response (const gchar *reply, GError **error); +/* +CGEV indication parser */ +typedef enum { + MM_3GPP_CGEV_UNKNOWN, + MM_3GPP_CGEV_NW_DETACH, + MM_3GPP_CGEV_ME_DETACH, + MM_3GPP_CGEV_NW_CLASS, + MM_3GPP_CGEV_ME_CLASS, + MM_3GPP_CGEV_NW_ACT_PRIMARY, + MM_3GPP_CGEV_ME_ACT_PRIMARY, + MM_3GPP_CGEV_NW_ACT_SECONDARY, + MM_3GPP_CGEV_ME_ACT_SECONDARY, + MM_3GPP_CGEV_NW_DEACT_PRIMARY, + MM_3GPP_CGEV_ME_DEACT_PRIMARY, + MM_3GPP_CGEV_NW_DEACT_SECONDARY, + MM_3GPP_CGEV_ME_DEACT_SECONDARY, + MM_3GPP_CGEV_NW_DEACT_PDP, + MM_3GPP_CGEV_ME_DEACT_PDP, + MM_3GPP_CGEV_NW_MODIFY, + MM_3GPP_CGEV_ME_MODIFY, + MM_3GPP_CGEV_REJECT, + MM_3GPP_CGEV_NW_REACT, +} MM3gppCgev; + +MM3gppCgev mm_3gpp_parse_cgev_indication_action (const gchar *str); +gboolean mm_3gpp_parse_cgev_indication_pdp (const gchar *str, + MM3gppCgev type, + gchar **out_pdp_type, + gchar **out_pdp_addr, + guint *out_cid, + GError **error); +gboolean mm_3gpp_parse_cgev_indication_primary (const gchar *str, + MM3gppCgev type, + guint *out_cid, + GError **error); +gboolean mm_3gpp_parse_cgev_indication_secondary (const gchar *str, + MM3gppCgev type, + guint *out_p_cid, + guint *out_cid, + guint *out_event_type, + GError **error); + /* AT+CMGL=4 (list sms parts) response parser */ typedef struct { gint index; diff --git a/src/tests/test-modem-helpers.c b/src/tests/test-modem-helpers.c index 598e53505..d5b09abdc 100644 --- a/src/tests/test-modem-helpers.c +++ b/src/tests/test-modem-helpers.c @@ -2045,6 +2045,161 @@ test_cind_response_moto_v3m (void *f, gpointer d) test_cind_results ("Motorola V3m", reply, &expected[0], G_N_ELEMENTS (expected)); } +/*****************************************************************************/ +/* Test +CGEV indication parsing */ + +typedef struct { + const gchar *str; + MM3gppCgev expected_type; + const gchar *expected_pdp_type; + const gchar *expected_pdp_addr; + guint expected_cid; + guint expected_parent_cid; + guint expected_event_type; +} CgevIndicationTest; + +static const CgevIndicationTest cgev_indication_tests[] = { + { "+CGEV: REJECT IP, 123.123.123.123", MM_3GPP_CGEV_REJECT, "IP", "123.123.123.123", 0, 0, 0 }, + { "REJECT IP, 123.123.123.123", MM_3GPP_CGEV_REJECT, "IP", "123.123.123.123", 0, 0, 0 }, + + { "+CGEV: NW REACT IP, 123.123.123.123", MM_3GPP_CGEV_NW_REACT, "IP", "123.123.123.123", 0, 0, 0 }, + { "NW REACT IP, 123.123.123.123", MM_3GPP_CGEV_NW_REACT, "IP", "123.123.123.123", 0, 0, 0 }, + { "+CGEV: NW REACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_NW_REACT, "IP", "123.123.123.123", 1, 0, 0 }, + + { "NW DEACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_NW_DEACT_PDP, "IP", "123.123.123.123", 1, 0, 0 }, + { "+CGEV: NW DEACT IP, 123.123.123.123", MM_3GPP_CGEV_NW_DEACT_PDP, "IP", "123.123.123.123", 0, 0, 0 }, + { "NW DEACT IP, 123.123.123.123", MM_3GPP_CGEV_NW_DEACT_PDP, "IP", "123.123.123.123", 0, 0, 0 }, + { "+CGEV: NW DEACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_NW_DEACT_PDP, "IP", "123.123.123.123", 1, 0, 0 }, + { "NW DEACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_NW_DEACT_PDP, "IP", "123.123.123.123", 1, 0, 0 }, + + { "ME DEACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_ME_DEACT_PDP, "IP", "123.123.123.123", 1, 0, 0 }, + { "+CGEV: ME DEACT IP, 123.123.123.123", MM_3GPP_CGEV_ME_DEACT_PDP, "IP", "123.123.123.123", 0, 0, 0 }, + { "ME DEACT IP, 123.123.123.123", MM_3GPP_CGEV_ME_DEACT_PDP, "IP", "123.123.123.123", 0, 0, 0 }, + { "+CGEV: ME DEACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_ME_DEACT_PDP, "IP", "123.123.123.123", 1, 0, 0 }, + { "ME DEACT IP, 123.123.123.123, 1", MM_3GPP_CGEV_ME_DEACT_PDP, "IP", "123.123.123.123", 1, 0, 0 }, + + { "ME PDN ACT 2", MM_3GPP_CGEV_ME_ACT_PRIMARY, NULL, NULL, 2, 0, 0 }, + { "+CGEV: ME PDN ACT 2", MM_3GPP_CGEV_ME_ACT_PRIMARY, NULL, NULL, 2, 0, 0 }, + /* with ,[,]] */ + { "ME PDN ACT 2, 3", MM_3GPP_CGEV_ME_ACT_PRIMARY, NULL, NULL, 2, 0, 0 }, + { "+CGEV: ME PDN ACT 2, 3", MM_3GPP_CGEV_ME_ACT_PRIMARY, NULL, NULL, 2, 0, 0 }, + { "ME PDN ACT 2, 3, 4", MM_3GPP_CGEV_ME_ACT_PRIMARY, NULL, NULL, 2, 0, 0 }, + { "+CGEV: ME PDN ACT 2, 3, 4", MM_3GPP_CGEV_ME_ACT_PRIMARY, NULL, NULL, 2, 0, 0 }, + + { "ME PDN DEACT 2", MM_3GPP_CGEV_ME_DEACT_PRIMARY, NULL, NULL, 2, 0, 0 }, + { "+CGEV: ME PDN DEACT 2", MM_3GPP_CGEV_ME_DEACT_PRIMARY, NULL, NULL, 2, 0, 0 }, + + { "ME ACT 3, 2, 1", MM_3GPP_CGEV_ME_ACT_SECONDARY, NULL, NULL, 2, 3, 1 }, + { "+CGEV: ME ACT 3, 2, 1", MM_3GPP_CGEV_ME_ACT_SECONDARY, NULL, NULL, 2, 3, 1 }, + + { "ME DEACT 3, 2, 1", MM_3GPP_CGEV_ME_DEACT_SECONDARY, NULL, NULL, 2, 3, 1 }, + { "+CGEV: ME DEACT 3, 2, 1", MM_3GPP_CGEV_ME_DEACT_SECONDARY, NULL, NULL, 2, 3, 1 }, + + { "NW PDN ACT 2", MM_3GPP_CGEV_NW_ACT_PRIMARY, NULL, NULL, 2, 0, 0 }, + { "+CGEV: NW PDN ACT 2", MM_3GPP_CGEV_NW_ACT_PRIMARY, NULL, NULL, 2, 0, 0 }, + + { "NW PDN DEACT 2", MM_3GPP_CGEV_NW_DEACT_PRIMARY, NULL, NULL, 2, 0, 0 }, + { "+CGEV: NW PDN DEACT 2", MM_3GPP_CGEV_NW_DEACT_PRIMARY, NULL, NULL, 2, 0, 0 }, + + { "NW ACT 3, 2, 1", MM_3GPP_CGEV_NW_ACT_SECONDARY, NULL, NULL, 2, 3, 1 }, + { "+CGEV: NW ACT 3, 2, 1", MM_3GPP_CGEV_NW_ACT_SECONDARY, NULL, NULL, 2, 3, 1 }, + + { "NW DEACT 3, 2, 1", MM_3GPP_CGEV_NW_DEACT_SECONDARY, NULL, NULL, 2, 3, 1 }, + { "+CGEV: NW DEACT 3, 2, 1", MM_3GPP_CGEV_NW_DEACT_SECONDARY, NULL, NULL, 2, 3, 1 }, + + { "+CGEV: NW DETACH", MM_3GPP_CGEV_NW_DETACH, NULL, NULL, 0, 0, 0 }, + { "NW DETACH", MM_3GPP_CGEV_NW_DETACH, NULL, NULL, 0, 0, 0 }, + { "+CGEV: ME DETACH", MM_3GPP_CGEV_ME_DETACH, NULL, NULL, 0, 0, 0 }, + { "ME DETACH", MM_3GPP_CGEV_ME_DETACH, NULL, NULL, 0, 0, 0 }, + + { "+CGEV: NW CLASS A", MM_3GPP_CGEV_NW_CLASS, NULL, NULL, 0, 0, 0 }, + { "NW CLASS A", MM_3GPP_CGEV_NW_CLASS, NULL, NULL, 0, 0, 0 }, + { "+CGEV: ME CLASS A", MM_3GPP_CGEV_ME_CLASS, NULL, NULL, 0, 0, 0 }, + { "ME CLASS A", MM_3GPP_CGEV_ME_CLASS, NULL, NULL, 0, 0, 0 }, +}; + +static void +test_cgev_indication (const CgevIndicationTest *t) +{ + guint i; + GError *error = NULL; + gboolean ret; + + for (i = 0; i < G_N_ELEMENTS (cgev_indication_tests); i++) { + const CgevIndicationTest *test = &cgev_indication_tests[i]; + MM3gppCgev type; + + type = mm_3gpp_parse_cgev_indication_action (test->str); + g_assert_cmpuint (type, ==, test->expected_type); + + g_print ("[%u] type: %u\n", i, type); + + switch (type) { + case MM_3GPP_CGEV_NW_DETACH: + case MM_3GPP_CGEV_ME_DETACH: + break; + case MM_3GPP_CGEV_NW_ACT_PRIMARY: + case MM_3GPP_CGEV_ME_ACT_PRIMARY: + case MM_3GPP_CGEV_NW_DEACT_PRIMARY: + case MM_3GPP_CGEV_ME_DEACT_PRIMARY: { + guint cid; + + g_print ("[%u] parsing as primary\n", i); + ret = mm_3gpp_parse_cgev_indication_primary (test->str, type, &cid, &error); + g_assert_no_error (error); + g_assert (ret); + g_assert_cmpuint (cid, ==, test->expected_cid); + break; + } + case MM_3GPP_CGEV_NW_ACT_SECONDARY: + case MM_3GPP_CGEV_ME_ACT_SECONDARY: + case MM_3GPP_CGEV_NW_DEACT_SECONDARY: + case MM_3GPP_CGEV_ME_DEACT_SECONDARY: { + guint p_cid; + guint cid; + guint event_type; + + g_print ("[%u] parsing as secondary\n", i); + ret = mm_3gpp_parse_cgev_indication_secondary (test->str, type, &p_cid, &cid, &event_type, &error); + g_assert_no_error (error); + g_assert (ret); + g_assert_cmpuint (cid, ==, test->expected_cid); + g_assert_cmpuint (p_cid, ==, test->expected_parent_cid); + g_assert_cmpuint (event_type, ==, test->expected_event_type); + break; + } + case MM_3GPP_CGEV_NW_DEACT_PDP: + case MM_3GPP_CGEV_ME_DEACT_PDP: + case MM_3GPP_CGEV_REJECT: + case MM_3GPP_CGEV_NW_REACT: { + gchar *pdp_type; + gchar *pdp_addr; + guint cid; + + g_print ("[%u] parsing as pdp\n", i); + ret = mm_3gpp_parse_cgev_indication_pdp (test->str, type, &pdp_type, &pdp_addr, &cid, &error); + g_assert_no_error (error); + g_assert (ret); + g_assert_cmpstr (pdp_type, ==, test->expected_pdp_type); + g_assert_cmpstr (pdp_addr, ==, test->expected_pdp_addr); + g_assert_cmpuint (cid, ==, test->expected_cid); + + g_free (pdp_type); + g_free (pdp_addr); + break; + } + case MM_3GPP_CGEV_NW_CLASS: + case MM_3GPP_CGEV_ME_CLASS: + case MM_3GPP_CGEV_NW_MODIFY: + case MM_3GPP_CGEV_ME_MODIFY: + /* ignore */ + break; + default: + break; + } + } +} + /*****************************************************************************/ /* Test ICCID parsing */ @@ -3999,6 +4154,8 @@ int main (int argc, char **argv) g_test_suite_add (suite, TESTCASE (test_cind_response_linktop_lw273, NULL)); g_test_suite_add (suite, TESTCASE (test_cind_response_moto_v3m, NULL)); + g_test_suite_add (suite, TESTCASE (test_cgev_indication, NULL)); + g_test_suite_add (suite, TESTCASE (test_iccid_parse_quoted_swap_19_digit, NULL)); g_test_suite_add (suite, TESTCASE (test_iccid_parse_unquoted_swap_20_digit, NULL)); g_test_suite_add (suite, TESTCASE (test_iccid_parse_unquoted_unswapped_19_digit, NULL)); -- cgit v1.2.1