aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@lanedo.com>2012-07-22 14:36:32 +0200
committerAleksander Morgado <aleksander@lanedo.com>2012-07-22 14:51:31 +0200
commitc6c1e0fb50569b9eafd92ba170a8dba42c4948be (patch)
tree519c35f944ed75d15cb0ecb9862fe7c1f3e30850
parent19e65253de194c8e6cdf1d1846729e2d778cf7ef (diff)
broadband-modem: fallback to unencoded command in USSD if encoded fails
-rw-r--r--src/mm-broadband-modem.c173
1 files changed, 139 insertions, 34 deletions
diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c
index d707363f..7f818fa3 100644
--- a/src/mm-broadband-modem.c
+++ b/src/mm-broadband-modem.c
@@ -130,6 +130,7 @@ struct _MMBroadbandModemPrivate {
/* Properties */
GObject *modem_3gpp_ussd_dbus_skeleton;
/* Implementation helpers */
+ gboolean use_unencoded_ussd;
GSimpleAsyncResult *pending_ussd_action;
/*<--- Modem CDMA interface --->*/
@@ -3387,6 +3388,29 @@ modem_3gpp_ussd_cancel (MMIfaceModem3gppUssd *self,
/*****************************************************************************/
/* Send command (3GPP/USSD interface) */
+typedef struct {
+ MMBroadbandModem *self;
+ GSimpleAsyncResult *result;
+ gchar *command;
+ gboolean current_is_unencoded;
+ gboolean encoded_used;
+ gboolean unencoded_used;
+} Modem3gppUssdSendContext;
+
+static void
+modem_3gpp_ussd_send_context_complete_and_free (Modem3gppUssdSendContext *ctx)
+{
+ /* We check for result, as we may have already set it in
+ * priv->pending_ussd_request */
+ if (ctx->result) {
+ g_simple_async_result_complete_in_idle (ctx->result);
+ g_object_unref (ctx->result);
+ }
+ g_object_unref (ctx->self);
+ g_free (ctx->command);
+ g_free (ctx);
+}
+
static const gchar *
modem_3gpp_ussd_send_finish (MMIfaceModem3gppUssd *self,
GAsyncResult *res,
@@ -3401,74 +3425,155 @@ modem_3gpp_ussd_send_finish (MMIfaceModem3gppUssd *self,
return (const gchar *)g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res));
}
+static void modem_3gpp_ussd_context_step (Modem3gppUssdSendContext *ctx);
+
static void
ussd_send_command_ready (MMBroadbandModem *self,
GAsyncResult *res,
- GSimpleAsyncResult *simple)
+ Modem3gppUssdSendContext *ctx)
{
GError *error = NULL;
mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, &error);
if (error) {
/* Some immediate error happened when sending the USSD request */
- g_simple_async_result_take_error (simple, error);
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ mm_dbg ("Error sending USSD request: '%s'", error->message);
+ g_error_free (error);
- mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self),
- MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE);
+ modem_3gpp_ussd_context_step (ctx);
return;
}
+ /* Cache the hint for the next time we send something */
+ if (!ctx->self->priv->use_unencoded_ussd &&
+ ctx->current_is_unencoded) {
+ mm_dbg ("Will assume we want unencoded USSD commands");
+ ctx->self->priv->use_unencoded_ussd = TRUE;
+ } else if (ctx->self->priv->use_unencoded_ussd &&
+ !ctx->current_is_unencoded) {
+ mm_dbg ("Will assume we want encoded USSD commands");
+ ctx->self->priv->use_unencoded_ussd = FALSE;
+ }
+
/* Cache the action, as it will be completed via URCs.
* There shouldn't be any previous action pending. */
g_warn_if_fail (self->priv->pending_ussd_action == NULL);
- self->priv->pending_ussd_action = simple;
+ self->priv->pending_ussd_action = ctx->result;
+
+ /* Reset result so that it doesn't get completed */
+ ctx->result = NULL;
+ modem_3gpp_ussd_send_context_complete_and_free (ctx);
}
static void
-modem_3gpp_ussd_send (MMIfaceModem3gppUssd *self,
- const gchar *command,
- GAsyncReadyCallback callback,
- gpointer user_data)
+modem_3gpp_ussd_context_send_encoded (Modem3gppUssdSendContext *ctx)
{
+ gchar *at_command = NULL;
GError *error = NULL;
- GSimpleAsyncResult *result;
- gchar *at_command;
- gchar *hex;
guint scheme = 0;
-
- /* We're going to steal the string result in finish() so we must have a
- * callback specified. */
- g_assert (callback != NULL);
- result = g_simple_async_result_new (G_OBJECT (self),
- callback,
- user_data,
- modem_3gpp_ussd_send);
+ gchar *encoded;
/* Encode USSD command */
- hex = mm_iface_modem_3gpp_ussd_encode (MM_IFACE_MODEM_3GPP_USSD (self),
- command,
- &scheme,
- &error);
- if (!hex) {
- g_simple_async_result_take_error (result, error);
- g_simple_async_result_complete_in_idle (result);
- g_object_unref (result);
+ encoded = mm_iface_modem_3gpp_ussd_encode (MM_IFACE_MODEM_3GPP_USSD (ctx->self),
+ ctx->command,
+ &scheme,
+ &error);
+ if (!encoded) {
+ g_simple_async_result_take_error (ctx->result, error);
+ modem_3gpp_ussd_send_context_complete_and_free (ctx);
+ mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (ctx->self),
+ MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE);
return;
}
/* Build AT command */
- at_command = g_strdup_printf ("+CUSD=1,\"%s\",%d", hex, scheme);
- g_free (hex);
+ ctx->encoded_used = TRUE;
+ ctx->current_is_unencoded = FALSE;
+ at_command = g_strdup_printf ("+CUSD=1,\"%s\",%d", encoded, scheme);
+ g_free (encoded);
- mm_base_modem_at_command (MM_BASE_MODEM (self),
+ mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
at_command,
3,
FALSE,
(GAsyncReadyCallback)ussd_send_command_ready,
- result);
+ ctx);
+ g_free (at_command);
+}
+
+static void
+modem_3gpp_ussd_context_send_unencoded (Modem3gppUssdSendContext *ctx)
+{
+ gchar *at_command = NULL;
+
+ /* Build AT command with action unencoded */
+ ctx->unencoded_used = TRUE;
+ ctx->current_is_unencoded = TRUE;
+ at_command = g_strdup_printf ("+CUSD=1,\"%s\",%d",
+ ctx->command,
+ MM_MODEM_GSM_USSD_SCHEME_7BIT);
+ mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
+ at_command,
+ 3,
+ FALSE,
+ (GAsyncReadyCallback)ussd_send_command_ready,
+ ctx);
g_free (at_command);
+}
+
+static void
+modem_3gpp_ussd_context_step (Modem3gppUssdSendContext *ctx)
+{
+ if (ctx->encoded_used &&
+ ctx->unencoded_used) {
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "Sending USSD command failed");
+ modem_3gpp_ussd_send_context_complete_and_free (ctx);
+
+ mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (ctx->self),
+ MM_MODEM_3GPP_USSD_SESSION_STATE_IDLE);
+ return;
+ }
+
+ if (ctx->self->priv->use_unencoded_ussd) {
+ if (!ctx->unencoded_used)
+ modem_3gpp_ussd_context_send_unencoded (ctx);
+ else if (!ctx->encoded_used)
+ modem_3gpp_ussd_context_send_encoded (ctx);
+ else
+ g_assert_not_reached ();
+ } else {
+ if (!ctx->encoded_used)
+ modem_3gpp_ussd_context_send_encoded (ctx);
+ else if (!ctx->unencoded_used)
+ modem_3gpp_ussd_context_send_unencoded (ctx);
+ else
+ g_assert_not_reached ();
+ }
+}
+
+static void
+modem_3gpp_ussd_send (MMIfaceModem3gppUssd *self,
+ const gchar *command,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ Modem3gppUssdSendContext *ctx;
+
+ ctx = g_new0 (Modem3gppUssdSendContext, 1);
+ /* We're going to steal the string result in finish() so we must have a
+ * callback specified. */
+ g_assert (callback != NULL);
+ ctx->result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ modem_3gpp_ussd_send);
+ ctx->self = g_object_ref (self);
+ ctx->command = g_strdup (command);
+
+ modem_3gpp_ussd_context_step (ctx);
mm_iface_modem_3gpp_ussd_update_state (MM_IFACE_MODEM_3GPP_USSD (self),
MM_MODEM_3GPP_USSD_SESSION_STATE_ACTIVE);