aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugins/ublox/mm-broadband-modem-ublox.c363
1 files changed, 325 insertions, 38 deletions
diff --git a/plugins/ublox/mm-broadband-modem-ublox.c b/plugins/ublox/mm-broadband-modem-ublox.c
index d27599bb..c0b3ef7d 100644
--- a/plugins/ublox/mm-broadband-modem-ublox.c
+++ b/plugins/ublox/mm-broadband-modem-ublox.c
@@ -69,6 +69,9 @@ struct _MMBroadbandModemUbloxPrivate {
/* Voice +UCALLSTAT support */
GRegex *ucallstat_regex;
+ FeatureSupport udtmfd_support;
+ GRegex *udtmfd_regex;
+
/* Regex to ignore */
GRegex *pbready_regex;
};
@@ -928,6 +931,213 @@ load_unlock_retries (MMIfaceModem *self,
}
/*****************************************************************************/
+/* Common enable/disable voice unsolicited events */
+
+typedef enum {
+ VOICE_UNSOLICITED_EVENTS_STEP_FIRST,
+ VOICE_UNSOLICITED_EVENTS_STEP_UCALLSTAT_PRIMARY,
+ VOICE_UNSOLICITED_EVENTS_STEP_UCALLSTAT_SECONDARY,
+ VOICE_UNSOLICITED_EVENTS_STEP_UDTMFD_PRIMARY,
+ VOICE_UNSOLICITED_EVENTS_STEP_UDTMFD_SECONDARY,
+ VOICE_UNSOLICITED_EVENTS_STEP_LAST,
+} VoiceUnsolicitedEventsStep;
+
+typedef struct {
+ gboolean enable;
+ VoiceUnsolicitedEventsStep step;
+ MMPortSerialAt *primary;
+ MMPortSerialAt *secondary;
+ gchar *ucallstat_command;
+ gchar *udtmfd_command;
+} VoiceUnsolicitedEventsContext;
+
+static void
+voice_unsolicited_events_context_free (VoiceUnsolicitedEventsContext *ctx)
+{
+ g_clear_object (&ctx->secondary);
+ g_clear_object (&ctx->primary);
+ g_free (ctx->ucallstat_command);
+ g_free (ctx->udtmfd_command);
+ g_slice_free (VoiceUnsolicitedEventsContext, ctx);
+}
+
+static gboolean
+common_voice_enable_disable_unsolicited_events_finish (MMBroadbandModemUblox *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void voice_unsolicited_events_context_step (GTask *task);
+
+static void
+udtmfd_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ VoiceUnsolicitedEventsContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_full_finish (self, res, &error)) {
+ mm_dbg ("Couldn't %s +UUDTMFD reporting: '%s'",
+ ctx->enable ? "enable" : "disable",
+ error->message);
+ g_error_free (error);
+ }
+
+ ctx->step++;
+ voice_unsolicited_events_context_step (task);
+}
+
+static void
+ucallstat_ready (MMBaseModem *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ VoiceUnsolicitedEventsContext *ctx;
+ GError *error = NULL;
+
+ ctx = g_task_get_task_data (task);
+
+ if (!mm_base_modem_at_command_full_finish (self, res, &error)) {
+ mm_dbg ("Couldn't %s +UCALLSTAT reporting: '%s'",
+ ctx->enable ? "enable" : "disable",
+ error->message);
+ g_error_free (error);
+ }
+
+ ctx->step++;
+ voice_unsolicited_events_context_step (task);
+}
+
+static void
+voice_unsolicited_events_context_step (GTask *task)
+{
+ MMBroadbandModemUblox *self;
+ VoiceUnsolicitedEventsContext *ctx;
+
+ self = MM_BROADBAND_MODEM_UBLOX (g_task_get_source_object (task));
+ ctx = g_task_get_task_data (task);
+
+ switch (ctx->step) {
+ case VOICE_UNSOLICITED_EVENTS_STEP_FIRST:
+ ctx->step++;
+ /* fall-through */
+
+ case VOICE_UNSOLICITED_EVENTS_STEP_UCALLSTAT_PRIMARY:
+ if (ctx->primary) {
+ mm_dbg ("%s extended call status reporting in primary port...",
+ ctx->enable ? "Enabling" : "Disabling");
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ ctx->primary,
+ ctx->ucallstat_command,
+ 3,
+ FALSE,
+ FALSE,
+ NULL,
+ (GAsyncReadyCallback)ucallstat_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
+
+ case VOICE_UNSOLICITED_EVENTS_STEP_UCALLSTAT_SECONDARY:
+ if (ctx->secondary) {
+ mm_dbg ("%s extended call status reporting in secondary port...",
+ ctx->enable ? "Enabling" : "Disabling");
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ ctx->secondary,
+ ctx->ucallstat_command,
+ 3,
+ FALSE,
+ FALSE,
+ NULL,
+ (GAsyncReadyCallback)ucallstat_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
+
+ case VOICE_UNSOLICITED_EVENTS_STEP_UDTMFD_PRIMARY:
+ if ((self->priv->udtmfd_support == FEATURE_SUPPORTED) && (ctx->primary)) {
+ mm_dbg ("%s DTMF detection and reporting in primary port...",
+ ctx->enable ? "Enabling" : "Disabling");
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ ctx->primary,
+ ctx->udtmfd_command,
+ 3,
+ FALSE,
+ FALSE,
+ NULL,
+ (GAsyncReadyCallback)udtmfd_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
+
+ case VOICE_UNSOLICITED_EVENTS_STEP_UDTMFD_SECONDARY:
+ if ((self->priv->udtmfd_support == FEATURE_SUPPORTED) && (ctx->secondary)) {
+ mm_dbg ("%s DTMF detection and reporting in secondary port...",
+ ctx->enable ? "Enabling" : "Disabling");
+ mm_base_modem_at_command_full (MM_BASE_MODEM (self),
+ ctx->secondary,
+ ctx->udtmfd_command,
+ 3,
+ FALSE,
+ FALSE,
+ NULL,
+ (GAsyncReadyCallback)udtmfd_ready,
+ task);
+ return;
+ }
+ ctx->step++;
+ /* fall-through */
+
+ case VOICE_UNSOLICITED_EVENTS_STEP_LAST:
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+common_voice_enable_disable_unsolicited_events (MMBroadbandModemUblox *self,
+ gboolean enable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ VoiceUnsolicitedEventsContext *ctx;
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ ctx = g_slice_new0 (VoiceUnsolicitedEventsContext);
+ ctx->step = VOICE_UNSOLICITED_EVENTS_STEP_FIRST;
+ ctx->enable = enable;
+ if (enable) {
+ ctx->ucallstat_command = g_strdup ("+UCALLSTAT=1");
+ ctx->udtmfd_command = g_strdup ("+UDTMFD=1,2");
+ } else {
+ ctx->ucallstat_command = g_strdup ("+UCALLSTAT=0");
+ ctx->udtmfd_command = g_strdup ("+UDTMFD=0");
+ }
+ 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) voice_unsolicited_events_context_free);
+
+ voice_unsolicited_events_context_step (task);
+}
+
+/*****************************************************************************/
/* Enabling unsolicited events (Voice interface) */
static gboolean
@@ -939,17 +1149,18 @@ modem_voice_enable_unsolicited_events_finish (MMIfaceModemVoice *self,
}
static void
-own_voice_enable_unsolicited_events_ready (MMBaseModem *self,
- GAsyncResult *res,
- GTask *task)
+voice_enable_unsolicited_events_ready (MMBroadbandModemUblox *self,
+ GAsyncResult *res,
+ GTask *task)
{
GError *error = NULL;
- mm_base_modem_at_command_full_finish (self, res, &error);
- if (error)
- g_task_return_error (task, error);
- else
- g_task_return_boolean (task, TRUE);
+ if (!common_voice_enable_disable_unsolicited_events_finish (self, res, &error)) {
+ mm_warn ("Couldn't enable u-blox-specific voice unsolicited events: %s", error->message);
+ g_error_free (error);
+ }
+
+ g_task_return_boolean (task, TRUE);
g_object_unref (task);
}
@@ -966,17 +1177,10 @@ parent_voice_enable_unsolicited_events_ready (MMIfaceModemVoice *self,
return;
}
- /* Our own enable now */
- mm_base_modem_at_command_full (
- MM_BASE_MODEM (self),
- mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
- "+UCALLSTAT=1",
- 5,
- FALSE, /* allow_cached */
- FALSE, /* raw */
- NULL, /* cancellable */
- (GAsyncReadyCallback)own_voice_enable_unsolicited_events_ready,
- task);
+ common_voice_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_UBLOX (self),
+ TRUE,
+ (GAsyncReadyCallback) voice_enable_unsolicited_events_ready,
+ task);
}
static void
@@ -1024,20 +1228,17 @@ parent_voice_disable_unsolicited_events_ready (MMIfaceModemVoice *self,
}
static void
-own_voice_disable_unsolicited_events_ready (MMBaseModem *self,
- GAsyncResult *res,
- GTask *task)
+voice_disable_unsolicited_events_ready (MMBroadbandModemUblox *self,
+ GAsyncResult *res,
+ GTask *task)
{
GError *error = NULL;
- mm_base_modem_at_command_full_finish (self, res, &error);
- if (error) {
- g_task_return_error (task, error);
- g_object_unref (task);
- return;
+ if (!common_voice_enable_disable_unsolicited_events_finish (self, res, &error)) {
+ mm_warn ("Couldn't disable u-blox-specific voice unsolicited events: %s", error->message);
+ g_error_free (error);
}
- /* Chain up parent's disable */
iface_modem_voice_parent->disable_unsolicited_events (
MM_IFACE_MODEM_VOICE (self),
(GAsyncReadyCallback)parent_voice_disable_unsolicited_events_ready,
@@ -1053,16 +1254,10 @@ modem_voice_disable_unsolicited_events (MMIfaceModemVoice *self,
task = g_task_new (self, NULL, callback, user_data);
- mm_base_modem_at_command_full (
- MM_BASE_MODEM (self),
- mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)),
- "+UCALLSTAT=0",
- 5,
- FALSE, /* allow_cached */
- FALSE, /* raw */
- NULL, /* cancellable */
- (GAsyncReadyCallback)own_voice_disable_unsolicited_events_ready,
- task);
+ common_voice_enable_disable_unsolicited_events (MM_BROADBAND_MODEM_UBLOX (self),
+ FALSE,
+ (GAsyncReadyCallback) voice_enable_unsolicited_events_ready,
+ task);
}
/*****************************************************************************/
@@ -1119,6 +1314,20 @@ ucallstat_received (MMPortSerialAt *port,
}
static void
+udtmfd_received (MMPortSerialAt *port,
+ GMatchInfo *match_info,
+ MMBroadbandModemUblox *self)
+{
+ gchar *dtmf;
+
+ dtmf = g_match_info_fetch (match_info, 1);
+ mm_dbg ("received DTMF: %s", dtmf);
+ /* call index unknown */
+ mm_iface_modem_voice_received_dtmf (MM_IFACE_MODEM_VOICE (self), 0, dtmf);
+ g_free (dtmf);
+}
+
+static void
common_voice_setup_cleanup_unsolicited_events (MMBroadbandModemUblox *self,
gboolean enable)
{
@@ -1129,6 +1338,10 @@ common_voice_setup_cleanup_unsolicited_events (MMBroadbandModemUblox *self,
self->priv->ucallstat_regex = g_regex_new ("\\r\\n\\+UCALLSTAT:\\s*(\\d+),(\\d+)\\r\\n",
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+ if (G_UNLIKELY (!self->priv->udtmfd_regex))
+ self->priv->udtmfd_regex = g_regex_new ("\\r\\n\\+UUDTMFD:\\s*([0-9A-D\\*\\#])\\r\\n",
+ G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
+
ports[0] = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self));
ports[1] = mm_base_modem_peek_port_secondary (MM_BASE_MODEM (self));
@@ -1141,6 +1354,12 @@ common_voice_setup_cleanup_unsolicited_events (MMBroadbandModemUblox *self,
enable ? (MMPortSerialAtUnsolicitedMsgFn)ucallstat_received : NULL,
enable ? self : NULL,
NULL);
+
+ mm_port_serial_at_add_unsolicited_msg_handler (ports[i],
+ self->priv->udtmfd_regex,
+ enable ? (MMPortSerialAtUnsolicitedMsgFn)udtmfd_received : NULL,
+ enable ? self : NULL,
+ NULL);
}
}
@@ -1253,6 +1472,69 @@ create_call (MMIfaceModemVoice *self,
}
/*****************************************************************************/
+/* Check if Voice supported (Voice interface) */
+
+static gboolean
+modem_voice_check_support_finish (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return g_task_propagate_boolean (G_TASK (res), error);
+}
+
+static void
+udtmfd_test_ready (MMBaseModem *_self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMBroadbandModemUblox *self = MM_BROADBAND_MODEM_UBLOX (_self);
+
+ self->priv->udtmfd_support = (!!mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, NULL) ?
+ FEATURE_SUPPORTED : FEATURE_UNSUPPORTED);
+
+ g_task_return_boolean (task, TRUE);
+ g_object_unref (task);
+}
+
+static void
+parent_voice_check_support_ready (MMIfaceModemVoice *self,
+ GAsyncResult *res,
+ GTask *task)
+{
+ GError *error = NULL;
+
+ if (!iface_modem_voice_parent->check_support_finish (self, res, &error)) {
+ g_task_return_error (task, error);
+ g_object_unref (task);
+ return;
+ }
+
+ /* voice is supported, check if +UDTMFD is available */
+ mm_base_modem_at_command (MM_BASE_MODEM (self),
+ "+UDTMFD=?",
+ 3,
+ TRUE,
+ (GAsyncReadyCallback) udtmfd_test_ready,
+ task);
+}
+
+static void
+modem_voice_check_support (MMIfaceModemVoice *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GTask *task;
+
+ task = g_task_new (self, NULL, callback, user_data);
+
+ /* chain up parent's setup first */
+ iface_modem_voice_parent->check_support (
+ self,
+ (GAsyncReadyCallback)parent_voice_check_support_ready,
+ task);
+}
+
+/*****************************************************************************/
/* Create Bearer (Modem interface) */
typedef enum {
@@ -1594,6 +1876,7 @@ mm_broadband_modem_ublox_init (MMBroadbandModemUblox *self)
self->priv->support_config.method = SETTINGS_UPDATE_METHOD_UNKNOWN;
self->priv->support_config.uact = FEATURE_SUPPORT_UNKNOWN;
self->priv->support_config.ubandsel = FEATURE_SUPPORT_UNKNOWN;
+ self->priv->udtmfd_support = FEATURE_SUPPORT_UNKNOWN;
self->priv->pbready_regex = g_regex_new ("\\r\\n\\+PBREADY\\r\\n",
G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL);
}
@@ -1643,6 +1926,8 @@ iface_modem_voice_init (MMIfaceModemVoice *iface)
{
iface_modem_voice_parent = g_type_interface_peek_parent (iface);
+ iface->check_support = modem_voice_check_support;
+ iface->check_support_finish = modem_voice_check_support_finish;
iface->setup_unsolicited_events = modem_voice_setup_unsolicited_events;
iface->setup_unsolicited_events_finish = modem_voice_setup_unsolicited_events_finish;
iface->cleanup_unsolicited_events = modem_voice_cleanup_unsolicited_events;
@@ -1664,6 +1949,8 @@ finalize (GObject *object)
if (self->priv->ucallstat_regex)
g_regex_unref (self->priv->ucallstat_regex);
+ if (self->priv->udtmfd_regex)
+ g_regex_unref (self->priv->udtmfd_regex);
g_free (self->priv->operator_id);
G_OBJECT_CLASS (mm_broadband_modem_ublox_parent_class)->finalize (object);