[PATCH 4/4] cinterion: use ^SIND unsolicited messages for access tech reporting
Aleksander Morgado
aleksander at aleksander.es
Sun May 21 19:49:51 UTC 2017
If the modem supports ^SIND psinfo reporting, we enable the URC and
flag the access technology polling unsupported, so that we only update
access technology via the +CIEV URCs.
E.g.:
(ttyACM1): --> 'AT^SIND="psinfo",1<CR>'
(ttyACM1): <-- '<CR><LF>^SIND: psinfo,1,10<CR><LF><CR><LF>OK<CR><LF>'
Reporting initial access technologies...
Modem /org/freedesktop/ModemManager1/Modem/0: access technology changed (unknown -> hsdpa, hsupa)
...
(ttyACM1): <-- '<CR><LF>+CIEV: psinfo,4<CR><LF>'
Modem /org/freedesktop/ModemManager1/Modem/0: access technology changed (hsdpa, hsupa -> edge)
...
---
plugins/cinterion/mm-broadband-modem-cinterion.c | 439 ++++++++++++++++-------
plugins/cinterion/mm-modem-helpers-cinterion.c | 33 ++
plugins/cinterion/mm-modem-helpers-cinterion.h | 5 +
3 files changed, 339 insertions(+), 138 deletions(-)
diff --git a/plugins/cinterion/mm-broadband-modem-cinterion.c b/plugins/cinterion/mm-broadband-modem-cinterion.c
index 735f15bc..06f4357f 100644
--- a/plugins/cinterion/mm-broadband-modem-cinterion.c
+++ b/plugins/cinterion/mm-broadband-modem-cinterion.c
@@ -61,7 +61,7 @@ typedef enum {
} FeatureSupport;
struct _MMBroadbandModemCinterionPrivate {
- /* Flag to know if we should try AT^SIND or not to get psinfo */
+ /* Flag to know if AT^SIND indications are supported */
gboolean sind_psinfo;
/* Command to go into sleep mode */
@@ -80,6 +80,9 @@ struct _MMBroadbandModemCinterionPrivate {
GArray *cnmi_supported_ds;
GArray *cnmi_supported_bfr;
+ /* +CIEV 'psinfo' indications */
+ GRegex *ciev_psinfo_regex;
+
/*Flags for SWWAN support*/
FeatureSupport swwan_support;
};
@@ -572,185 +575,329 @@ modem_power_off (MMIfaceModem *self,
}
/*****************************************************************************/
-/* ACCESS TECHNOLOGIES */
+/* Access technologies polling */
static gboolean
-load_access_technologies_finish (MMIfaceModem *self,
- GAsyncResult *res,
- MMModemAccessTechnology *access_technologies,
- guint *mask,
- GError **error)
+load_access_technologies_finish (MMIfaceModem *self,
+ GAsyncResult *res,
+ MMModemAccessTechnology *access_technologies,
+ guint *mask,
+ GError **error)
{
- if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
+ gssize val;
+
+ if ((val = g_task_propagate_int (G_TASK (res), error)) < 0)
return FALSE;
- *access_technologies = (MMModemAccessTechnology) GPOINTER_TO_UINT (
- g_simple_async_result_get_op_res_gpointer (
- G_SIMPLE_ASYNC_RESULT (res)));
+ *access_technologies = (MMModemAccessTechnology) val;
*mask = MM_MODEM_ACCESS_TECHNOLOGY_ANY;
return TRUE;
}
static void
-smong_query_ready (MMBroadbandModemCinterion *self,
+smong_query_ready (MMBaseModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+ GTask *task)
{
const gchar *response;
GError *error = NULL;
MMModemAccessTechnology access_tech;
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
- if (!response) {
- /* Let the error be critical. */
- g_simple_async_result_take_error (operation_result, error);
- } else if (!mm_cinterion_parse_smong_response (response, &access_tech, &error)) {
- /* We'll reset here the flag to try to use SIND/psinfo the next time */
- self->priv->sind_psinfo = TRUE;
- g_simple_async_result_take_error (operation_result, error);
- } else {
- /* We'll default to use SMONG then */
- self->priv->sind_psinfo = FALSE;
- g_simple_async_result_set_op_res_gpointer (operation_result, GUINT_TO_POINTER (access_tech), NULL);
+ response = mm_base_modem_at_command_finish (self, res, &error);
+ if (!response || !mm_cinterion_parse_smong_response (response, &access_tech, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_int (task, (gssize) access_tech);
+ g_object_unref (task);
+}
+
+static void
+load_access_technologies (MMIfaceModem *_self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemCinterion *self = MM_BROADBAND_MODEM_CINTERION (_self);
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Abort access technology polling if ^SIND psinfo URCs are enabled */
+ if (self->priv->sind_psinfo) {
+ g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED, "No need to poll access technologies");
+ g_object_unref (task);
+ return;
}
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ mm_base_modem_at_command (
+ MM_BASE_MODEM (self),
+ "^SMONG",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)smong_query_ready,
+ task);
}
-static MMModemAccessTechnology
-get_access_technology_from_psinfo (const gchar *psinfo,
- GError **error)
-{
- guint psinfoval;
-
- if (mm_get_uint_from_str (psinfo, &psinfoval)) {
- switch (psinfoval) {
- case 0:
- return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
- case 1:
- case 2:
- return MM_MODEM_ACCESS_TECHNOLOGY_GPRS;
- case 3:
- case 4:
- return MM_MODEM_ACCESS_TECHNOLOGY_EDGE;
- case 5:
- case 6:
- return MM_MODEM_ACCESS_TECHNOLOGY_UMTS;
- case 7:
- case 8:
- return MM_MODEM_ACCESS_TECHNOLOGY_HSDPA;
- case 9:
- case 10:
- return (MM_MODEM_ACCESS_TECHNOLOGY_HSDPA | MM_MODEM_ACCESS_TECHNOLOGY_HSUPA);
- case 16:
- case 17:
- return MM_MODEM_ACCESS_TECHNOLOGY_LTE;
- default:
- mm_dbg ("Unable to identify access technology in case:%i", psinfoval);
- break;
- }
+/*****************************************************************************/
+/* Disable unsolicited events (3GPP interface) */
+
+static gboolean
+modem_3gpp_disable_unsolicited_events_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+parent_disable_unsolicited_events_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!iface_modem_3gpp_parent->disable_unsolicited_events_finish (self, res, &error)) {
+ mm_warn ("Couldn't disable parent 3GPP unsolicited events: %s", error->message);
+ g_error_free (error);
}
- else
- mm_err ("FAILED get_access_technology_from_psinfo-int");
- g_set_error (error,
- MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Couldn't get network capabilities, "
- "invalid psinfo value: '%s'",
- psinfo);
- return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-sind_query_ready (MMBroadbandModemCinterion *self,
- GAsyncResult *res,
- GSimpleAsyncResult *operation_result)
+sind_psinfo_disable_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
{
- const gchar *response;
GError *error = NULL;
- GMatchInfo *match_info = NULL;
- GRegex *regex;
- response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
- if (!response) {
- /* Let the error be critical. */
- g_simple_async_result_take_error (operation_result, error);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
- return;
+ if (!mm_base_modem_at_command_finish (self, res, &error)) {
+ mm_warn ("Couldn't disable ^SIND psinfo notifications: %s", error->message);
+ g_error_free (error);
}
- /* The AT^SIND? command replies a list of several different indicators.
- * We will only look for 'psinfo' which is the one which may tell us
- * the available network access technology. Note that only 3G-enabled
- * devices seem to have this indicator.
- *
- * AT+SIND?
- * ^SIND: battchg,1,1
- * ^SIND: signal,1,99
- * ...
- */
- regex = g_regex_new ("\\r\\n\\^SIND:\\s*psinfo,\\s*(\\d*),\\s*(\\d*)", 0, 0, NULL);
- if (g_regex_match_full (regex, response, strlen (response), 0, 0, &match_info, NULL)) {
- MMModemAccessTechnology act;
- gchar *ind_value;
-
- ind_value = g_match_info_fetch (match_info, 2);
- act = get_access_technology_from_psinfo (ind_value, &error);
- g_free (ind_value);
- g_simple_async_result_set_op_res_gpointer (operation_result, GUINT_TO_POINTER (act), NULL);
- g_simple_async_result_complete (operation_result);
- g_object_unref (operation_result);
+ /* Chain up parent's disabñe */
+ iface_modem_3gpp_parent->disable_unsolicited_events (
+ MM_IFACE_MODEM_3GPP (self),
+ (GAsyncReadyCallback)parent_disable_unsolicited_events_ready,
+ task);
+}
+
+static void
+modem_3gpp_disable_unsolicited_events (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Disable access technology update reporting */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "AT^SIND=\"psinfo\",0",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)sind_psinfo_disable_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Enable unsolicited events (3GPP interface) */
+
+static gboolean
+modem_3gpp_enable_unsolicited_events_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+sind_psinfo_enable_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemCinterion *self;
+ GError *error = NULL;
+ const gchar *response;
+ guint mode;
+ guint val;
+
+ self = MM_BROADBAND_MODEM_CINTERION (_self);
+ if (!(response = mm_base_modem_at_command_finish (_self, res, &error))) {
+ mm_warn ("Couldn't enable ^SIND psinfo notifications: %s", error->message);
+ g_error_free (error);
+ } else if (!mm_cinterion_parse_sind_response (response, NULL, &mode, &val, &error)) {
+ mm_warn ("Couldn't parse ^SIND psinfo response: %s", error->message);
+ g_error_free (error);
} else {
- /* If there was no 'psinfo' indicator, we'll try AT^SMONG and read the cell
- * info table. */
- mm_base_modem_at_command (
- MM_BASE_MODEM (self),
- "^SMONG",
- 3,
- FALSE,
- (GAsyncReadyCallback)smong_query_ready,
- operation_result);
+ /* If enabled the indications, flag ^SIND psinfo supported so that we don't poll */
+ self->priv->sind_psinfo = TRUE;
+
+ /* Report initial access technology gathered right away */
+ mm_dbg ("Reporting initial access technologies...");
+ mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self),
+ mm_cinterion_get_access_technology_from_sind_psinfo (val),
+ MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK);
}
- g_match_info_free (match_info);
- g_regex_unref (regex);
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
}
static void
-load_access_technologies (MMIfaceModem *self,
- GAsyncReadyCallback callback,
- gpointer user_data)
+parent_enable_unsolicited_events_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GTask *task)
{
- MMBroadbandModemCinterion *broadband = MM_BROADBAND_MODEM_CINTERION (self);
- GSimpleAsyncResult *result;
+ GError *error = NULL;
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- load_access_technologies);
+ if (!iface_modem_3gpp_parent->enable_unsolicited_events_finish (self, res, &error)) {
+ mm_warn ("Couldn't enable parent 3GPP unsolicited events: %s", error->message);
+ g_error_free (error);
+ }
- if (broadband->priv->sind_psinfo) {
- /* TODO: Trigger off psinfo URC instead of this polling. */
- mm_base_modem_at_command (
- MM_BASE_MODEM (self),
- "^SIND?",
- 3,
- FALSE,
- (GAsyncReadyCallback)sind_query_ready,
- result);
+ /* Enable access technology update reporting */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "AT^SIND=\"psinfo\",1",
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)sind_psinfo_enable_ready,
+ task);
+}
+
+static void
+modem_3gpp_enable_unsolicited_events (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Chain up parent's enable */
+ iface_modem_3gpp_parent->enable_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_enable_unsolicited_events_ready,
+ task);
+}
+
+/*****************************************************************************/
+/* Setup/Cleanup unsolicited events (3GPP interface) */
+
+static void
+sind_psinfo_received (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMBroadbandModemCinterion *self)
+{
+ guint val;
+
+ if (!mm_get_uint_from_match_info (match_info, 1, &val)) {
+ mm_dbg ("Failed to convert psinfo value");
return;
}
- mm_base_modem_at_command (
- MM_BASE_MODEM (self),
- "^SMONG",
- 3,
- FALSE,
- (GAsyncReadyCallback)smong_query_ready,
- result);
+ mm_iface_modem_update_access_technologies (MM_IFACE_MODEM (self),
+ mm_cinterion_get_access_technology_from_sind_psinfo (val),
+ MM_IFACE_MODEM_3GPP_ALL_ACCESS_TECHNOLOGIES_MASK);
+}
+
+static void
+set_unsolicited_events_handlers (MMBroadbandModemCinterion *self,
+ gboolean enable)
+{
+ MMPortSerialAt *ports[2];
+ guint i;
+
+ 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;
+
+ mm_port_serial_at_add_unsolicited_msg_handler (
+ ports[i],
+ self->priv->ciev_psinfo_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn)sind_psinfo_received : NULL,
+ enable ? self : NULL,
+ NULL);
+ }
+}
+
+static gboolean
+modem_3gpp_setup_cleanup_unsolicited_events_finish (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+parent_setup_unsolicited_events_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!iface_modem_3gpp_parent->setup_unsolicited_events_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else {
+ /* Our own setup now */
+ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_CINTERION (self), TRUE);
+ g_task_return_boolean (task, TRUE);
+ }
+ g_object_unref (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);
+
+ /* Chain up parent's setup */
+ iface_modem_3gpp_parent->setup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_setup_unsolicited_events_ready,
+ task);
+}
+
+static void
+parent_cleanup_unsolicited_events_ready (MMIfaceModem3gpp *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!iface_modem_3gpp_parent->cleanup_unsolicited_events_finish (self, res, &error))
+ g_task_return_error (task, error);
+ else
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+modem_3gpp_cleanup_unsolicited_events (MMIfaceModem3gpp *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* Our own cleanup first */
+ set_unsolicited_events_handlers (MM_BROADBAND_MODEM_CINTERION (self), FALSE);
+
+ /* And now chain up parent's cleanup */
+ iface_modem_3gpp_parent->cleanup_unsolicited_events (
+ self,
+ (GAsyncReadyCallback)parent_cleanup_unsolicited_events_ready,
+ task);
}
/*****************************************************************************/
@@ -1733,6 +1880,9 @@ mm_broadband_modem_cinterion_init (MMBroadbandModemCinterion *self)
/* Initialize private variables */
self->priv->sind_psinfo = TRUE; /* Initially, always try to get psinfo */
self->priv->swwan_support = FEATURE_SUPPORT_UNKNOWN;
+
+ self->priv->ciev_psinfo_regex = g_regex_new ("\\r\\n\\+CIEV: psinfo,(\\d+)\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
}
static void
@@ -1754,6 +1904,8 @@ finalize (GObject *object)
if (self->priv->cnmi_supported_bfr)
g_array_unref (self->priv->cnmi_supported_bfr);
+ g_regex_unref (self->priv->ciev_psinfo_regex);
+
G_OBJECT_CLASS (mm_broadband_modem_cinterion_parent_class)->finalize (object);
}
@@ -1792,6 +1944,17 @@ static void
iface_modem_3gpp_init (MMIfaceModem3gpp *iface)
{
iface_modem_3gpp_parent = g_type_interface_peek_parent (iface);
+
+ iface->enable_unsolicited_events = modem_3gpp_enable_unsolicited_events;
+ iface->enable_unsolicited_events_finish = modem_3gpp_enable_unsolicited_events_finish;
+ iface->disable_unsolicited_events = modem_3gpp_disable_unsolicited_events;
+ iface->disable_unsolicited_events_finish = modem_3gpp_disable_unsolicited_events_finish;
+
+ iface->setup_unsolicited_events = modem_3gpp_setup_unsolicited_events;
+ iface->setup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish;
+ iface->cleanup_unsolicited_events = modem_3gpp_cleanup_unsolicited_events;
+ iface->cleanup_unsolicited_events_finish = modem_3gpp_setup_cleanup_unsolicited_events_finish;
+
iface->register_in_network = register_in_network;
iface->register_in_network_finish = register_in_network_finish;
}
diff --git a/plugins/cinterion/mm-modem-helpers-cinterion.c b/plugins/cinterion/mm-modem-helpers-cinterion.c
index da1e7787..3c7a0a0c 100644
--- a/plugins/cinterion/mm-modem-helpers-cinterion.c
+++ b/plugins/cinterion/mm-modem-helpers-cinterion.c
@@ -624,3 +624,36 @@ mm_cinterion_parse_smong_response (const gchar *response,
g_assert (access_tech != MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN);
return TRUE;
}
+
+/*****************************************************************************/
+/* ^SIND psinfo helper */
+
+MMModemAccessTechnology
+mm_cinterion_get_access_technology_from_sind_psinfo (guint val)
+{
+ switch (val) {
+ case 0:
+ return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+ case 1:
+ case 2:
+ return MM_MODEM_ACCESS_TECHNOLOGY_GPRS;
+ case 3:
+ case 4:
+ return MM_MODEM_ACCESS_TECHNOLOGY_EDGE;
+ case 5:
+ case 6:
+ return MM_MODEM_ACCESS_TECHNOLOGY_UMTS;
+ case 7:
+ case 8:
+ return MM_MODEM_ACCESS_TECHNOLOGY_HSDPA;
+ case 9:
+ case 10:
+ return (MM_MODEM_ACCESS_TECHNOLOGY_HSDPA | MM_MODEM_ACCESS_TECHNOLOGY_HSUPA);
+ case 16:
+ case 17:
+ return MM_MODEM_ACCESS_TECHNOLOGY_LTE;
+ default:
+ mm_dbg ("Unable to identify access technology from psinfo reported value: %u", val);
+ return MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN;
+ }
+}
diff --git a/plugins/cinterion/mm-modem-helpers-cinterion.h b/plugins/cinterion/mm-modem-helpers-cinterion.h
index bcf02f9d..2ec05157 100644
--- a/plugins/cinterion/mm-modem-helpers-cinterion.h
+++ b/plugins/cinterion/mm-modem-helpers-cinterion.h
@@ -82,4 +82,9 @@ gboolean mm_cinterion_parse_smong_response (const gchar *response,
MMModemAccessTechnology *access_tech,
GError **error);
+/*****************************************************************************/
+/* ^SIND psinfo helper */
+
+MMModemAccessTechnology mm_cinterion_get_access_technology_from_sind_psinfo (guint val);
+
#endif /* MM_MODEM_HELPERS_CINTERION_H */
--
2.12.2
More information about the ModemManager-devel
mailing list