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 _______________________________________________ ModemManager-devel mailing list ModemManager-devel@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/modemmanager-devel