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.
This commit is contained in:

committed by
Dan Williams

parent
73df7087fd
commit
a15193b7ca
@@ -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,7 +2966,7 @@ ciev_received (MMPortSerialAt *port,
|
||||
}
|
||||
|
||||
static void
|
||||
set_unsolicited_events_handlers (MMBroadbandModem *self,
|
||||
set_ciev_unsolicited_events_handlers (MMBroadbandModem *self,
|
||||
gboolean enable)
|
||||
{
|
||||
MMPortSerialAt *ports[2];
|
||||
@@ -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 (
|
||||
@@ -2732,6 +2997,48 @@ set_unsolicited_events_handlers (MMBroadbandModem *self,
|
||||
g_regex_unref (ciev_regex);
|
||||
}
|
||||
|
||||
static void
|
||||
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,
|
||||
@@ -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,11 +3094,9 @@ 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
|
||||
@@ -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,12 +3188,31 @@ 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_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
|
||||
@@ -2906,9 +3225,11 @@ modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *_self,
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -2949,22 +3280,17 @@ unsolicited_events_setup_ready (MMBroadbandModem *self,
|
||||
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;
|
||||
}
|
||||
|
||||
ctx = g_task_get_task_data (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);
|
||||
/* Consider this operation complete, ignoring errors */
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
/* Continue on next port/command */
|
||||
run_unsolicited_events_setup (task);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -2973,23 +3299,45 @@ run_unsolicited_events_setup (GTask *task)
|
||||
MMBroadbandModem *self;
|
||||
UnsolicitedEventsContext *ctx;
|
||||
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);
|
||||
}
|
||||
@@ -3011,32 +3359,23 @@ modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *_self,
|
||||
{
|
||||
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->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);
|
||||
|
||||
run_unsolicited_events_setup (task);
|
||||
return;
|
||||
}
|
||||
mm_dbg ("Skipping +CMER enable command: not supported");
|
||||
}
|
||||
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);
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
if (self->priv->modem_cgerep_support_checked && self->priv->modem_cgerep_supported)
|
||||
ctx->cgerep_command = g_strdup ("+CGEREP=2");
|
||||
|
||||
run_unsolicited_events_setup (task);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -3046,31 +3385,22 @@ modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *_self,
|
||||
{
|
||||
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->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);
|
||||
|
||||
run_unsolicited_events_setup (task);
|
||||
return;
|
||||
}
|
||||
mm_dbg ("Skipping +CMER disable command: not supported");
|
||||
}
|
||||
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);
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
if (self->priv->modem_cgerep_support_checked && self->priv->modem_cgerep_supported)
|
||||
ctx->cgerep_command = g_strdup ("+CGEREP=0");
|
||||
|
||||
run_unsolicited_events_setup (task);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
@@ -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 <cid>
|
||||
* +CGEV: ME PDN ACT <cid>[,<reason>[,<cid_other>]]
|
||||
* +CGEV: NW ACT <p_cid>, <cid>, <event_type>
|
||||
* +CGEV: ME ACT <p_cid>, <cid>, <event_type>
|
||||
* +CGEV: NW DEACT <PDP_type>, <PDP_addr>, [<cid>]
|
||||
* +CGEV: ME DEACT <PDP_type>, <PDP_addr>, [<cid>]
|
||||
* +CGEV: NW PDN DEACT <cid>
|
||||
* +CGEV: ME PDN DEACT <cid>
|
||||
* +CGEV: NW DEACT <p_cid>, <cid>, <event_type>
|
||||
* +CGEV: ME DEACT <p_cid>, <cid>, <event_type>
|
||||
* +CGEV: REJECT <PDP_type>, <PDP_addr>
|
||||
* +CGEV: NW REACT <PDP_type>, <PDP_addr>, [<cid>]
|
||||
*
|
||||
* We don't provide parameter parsing for these messages:
|
||||
* +CGEV: NW CLASS <class>
|
||||
* +CGEV: ME CLASS <class>
|
||||
* +CGEV: NW MODIFY <cid>, <change_reason>, <event_type>
|
||||
* +CGEV: ME MODIFY <cid>, <change_reason>, <event_type>
|
||||
*/
|
||||
|
||||
static gboolean
|
||||
deact_secondary (const gchar *str)
|
||||
{
|
||||
/* We need to detect the ME/NW DEACT format.
|
||||
* Either,
|
||||
* +CGEV: NW DEACT <PDP_type>, <PDP_addr>, [<cid>]
|
||||
* +CGEV: ME DEACT <PDP_type>, <PDP_addr>, [<cid>]
|
||||
* or,
|
||||
* +CGEV: NW DEACT <p_cid>, <cid>, <event_type>
|
||||
* +CGEV: ME DEACT <p_cid>, <cid>, <event_type>
|
||||
*/
|
||||
str = strstr (str, "DEACT") + 5;
|
||||
while (*str == ' ')
|
||||
str++;
|
||||
|
||||
/* We will look for <p_cid> 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 <PDP_type>, <PDP_addr>, [<cid>]
|
||||
* +CGEV: ME DEACT <PDP_type>, <PDP_addr>, [<cid>]
|
||||
* +CGEV: REJECT <PDP_type>, <PDP_addr>
|
||||
* +CGEV: NW REACT <PDP_type>, <PDP_addr>, [<cid>]
|
||||
*/
|
||||
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 <cid>
|
||||
* +CGEV: ME PDN ACT <cid>[,<reason>[,<cid_other>]]
|
||||
* +CGEV: NW PDN DEACT <cid>
|
||||
* +CGEV: ME PDN DEACT <cid>
|
||||
*
|
||||
* NOTE: the special case of a "ME PDN ACT" notification with the additional
|
||||
* <reason> and <cid_other> fields is telling us that <cid> was NOT connected
|
||||
* but <cid_other> 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
|
||||
* <cid> 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 <p_cid>, <cid>, <event_type>
|
||||
* +CGEV: ME ACT <p_cid>, <cid>, <event_type>
|
||||
* +CGEV: NW DEACT <p_cid>, <cid>, <event_type>
|
||||
* +CGEV: ME DEACT <p_cid>, <cid>, <event_type>
|
||||
*/
|
||||
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
|
||||
|
@@ -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;
|
||||
|
@@ -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 ,<reason>[,<cid_other>]] */
|
||||
{ "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));
|
||||
|
Reference in New Issue
Block a user