hso: setup connection sequence

This commit is contained in:
Aleksander Morgado
2012-03-16 14:53:26 +01:00
parent fe13d4cb66
commit 7de8b588b6
2 changed files with 379 additions and 0 deletions
+369
View File
@@ -46,8 +46,373 @@ static GParamSpec *properties[PROP_LAST];
struct _MMBroadbandBearerHsoPrivate {
gchar *user;
gchar *password;
guint auth_idx;
gpointer connect_pending;
guint connect_pending_id;
gulong connect_cancellable_id;
};
/*****************************************************************************/
/* 3GPP Dialing (sub-step of the 3GPP Connection sequence) */
typedef struct {
MMBroadbandBearerHso *self;
MMBaseModem *modem;
MMAtSerialPort *primary;
guint cid;
GCancellable *cancellable;
GSimpleAsyncResult *result;
guint auth_idx;
} Dial3gppContext;
static Dial3gppContext *
dial_3gpp_context_new (MMBroadbandBearerHso *self,
MMBaseModem *modem,
MMAtSerialPort *primary,
guint cid,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
Dial3gppContext *ctx;
ctx = g_new0 (Dial3gppContext, 1);
ctx->self = g_object_ref (self);
ctx->modem = g_object_ref (modem);
ctx->primary = g_object_ref (primary);
ctx->cid = cid;
ctx->result = g_simple_async_result_new (G_OBJECT (self),
callback,
user_data,
dial_3gpp_context_new);
ctx->cancellable = g_object_ref (cancellable);
/* Always start with the index that worked last time
* (will be 0 the first time)*/
ctx->auth_idx = self->priv->auth_idx;
return ctx;
}
static void
dial_3gpp_context_complete_and_free (Dial3gppContext *ctx)
{
g_simple_async_result_complete (ctx->result);
g_object_unref (ctx->cancellable);
g_object_unref (ctx->result);
g_object_unref (ctx->primary);
g_object_unref (ctx->modem);
g_object_unref (ctx->self);
g_free (ctx);
}
static gboolean
dial_3gpp_context_set_error_if_cancelled (Dial3gppContext *ctx,
GError **error)
{
if (!g_cancellable_is_cancelled (ctx->cancellable))
return FALSE;
g_set_error (error,
MM_CORE_ERROR,
MM_CORE_ERROR_CANCELLED,
"Dial operation has been cancelled");
return TRUE;
}
static gboolean
dial_3gpp_context_complete_and_free_if_cancelled (Dial3gppContext *ctx)
{
GError *error = NULL;
if (!dial_3gpp_context_set_error_if_cancelled (ctx, &error))
return FALSE;
g_simple_async_result_take_error (ctx->result, error);
dial_3gpp_context_complete_and_free (ctx);
return TRUE;
}
static gboolean
dial_3gpp_finish (MMBroadbandBearer *self,
GAsyncResult *res,
GError **error)
{
return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
}
void
mm_broadband_bearer_hso_report_connection_status (MMBroadbandBearerHso *self,
MMBroadbandBearerHsoConnectionStatus status)
{
Dial3gppContext *ctx;
/* Recover context (if any) and remove both cancellation and timeout (if any)*/
ctx = self->priv->connect_pending;
self->priv->connect_pending = NULL;
if (self->priv->connect_pending_id) {
g_source_remove (self->priv->connect_pending_id);
self->priv->connect_pending_id = 0;
}
if (self->priv->connect_cancellable_id) {
g_cancellable_disconnect (ctx->cancellable,
self->priv->connect_cancellable_id);
self->priv->connect_cancellable_id = 0;
}
switch (status) {
case MM_BROADBAND_BEARER_HSO_CONNECTION_STATUS_UNKNOWN:
g_warn_if_reached ();
break;
case MM_BROADBAND_BEARER_HSO_CONNECTION_STATUS_CONNECTED:
if (!ctx)
break;
g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
dial_3gpp_context_complete_and_free (ctx);
return;
case MM_BROADBAND_BEARER_HSO_CONNECTION_STATUS_CONNECTION_FAILED:
if (!ctx)
break;
g_simple_async_result_set_error (ctx->result,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Call setup failed");
dial_3gpp_context_complete_and_free (ctx);
return;
case MM_BROADBAND_BEARER_HSO_CONNECTION_STATUS_DISCONNECTED:
if (!ctx) {
g_simple_async_result_set_error (ctx->result,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Call setup failed");
dial_3gpp_context_complete_and_free (ctx);
} else {
/* Just ensure we mark ourselves as being disconnected... */
g_object_set (self,
MM_BEARER_STATUS, MM_BEARER_STATUS_DISCONNECTED,
NULL);
}
break;
}
}
static void
connect_reset_ready (MMBaseModem *modem,
GAsyncResult *res,
Dial3gppContext *ctx)
{
mm_base_modem_at_command_full_finish (modem, res, NULL);
/* error should have already been set in the simple async result */
dial_3gpp_context_complete_and_free (ctx);
}
static void
connect_reset (Dial3gppContext *ctx)
{
gchar *command;
/* Need to reset the connection attempt */
command = g_strdup_printf ("AT_OWANCALL=%d,0,1",
ctx->cid);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
command,
3,
FALSE,
NULL, /* cancellable */
(GAsyncReadyCallback)connect_reset_ready,
ctx);
g_free (command);
}
static gboolean
connect_timed_out_cb (MMBroadbandBearerHso *self)
{
Dial3gppContext *ctx;
/* Recover context and remove cancellation */
ctx = self->priv->connect_pending;
g_cancellable_disconnect (ctx->cancellable,
self->priv->connect_cancellable_id);
self->priv->connect_pending = NULL;
self->priv->connect_pending_id = 0;
self->priv->connect_cancellable_id = 0;
g_simple_async_result_set_error (ctx->result,
MM_MOBILE_EQUIPMENT_ERROR,
MM_MOBILE_EQUIPMENT_ERROR_NETWORK_TIMEOUT,
"Connection attempt timed out");
connect_reset (ctx);
return FALSE;
}
static void
connect_cancelled_cb (GCancellable *cancellable,
MMBroadbandBearerHso *self)
{
GError *error = NULL;
Dial3gppContext *ctx;
/* Recover context and remove timeout */
ctx = self->priv->connect_pending;
g_source_remove (self->priv->connect_pending_id);
self->priv->connect_pending = NULL;
self->priv->connect_pending_id = 0;
self->priv->connect_cancellable_id = 0;
g_assert (dial_3gpp_context_set_error_if_cancelled (ctx, &error));
g_simple_async_result_take_error (ctx->result, error);
connect_reset (ctx);
}
static void
activate_ready (MMBaseModem *modem,
GAsyncResult *res,
Dial3gppContext *ctx)
{
GError *error = NULL;
/* From now on, if we get cancelled, we'll need to run the connection
* reset ourselves just in case */
if (!mm_base_modem_at_command_full_finish (modem, res, &error)) {
g_simple_async_result_take_error (ctx->result, error);
g_simple_async_result_complete (ctx->result);
g_object_unref (ctx->result);
return;
}
/* We will now setup a timeout and keep the context in the bearer's private.
* Reports of modem being connected will arrive via unsolicited messages. */
g_assert (ctx->self->priv->connect_pending == NULL);
ctx->self->priv->connect_pending = ctx;
ctx->self->priv->connect_pending_id = g_timeout_add_seconds (30,
(GSourceFunc)connect_timed_out_cb,
ctx->self);
ctx->self->priv->connect_cancellable_id = g_cancellable_connect (ctx->cancellable,
G_CALLBACK (connect_cancelled_cb),
ctx->self,
NULL);
}
static void authenticate (Dial3gppContext *ctx);
static void
authenticate_ready (MMBaseModem *modem,
GAsyncResult *res,
Dial3gppContext *ctx)
{
gchar *command;
/* If cancelled, complete */
if (dial_3gpp_context_complete_and_free_if_cancelled (ctx))
return;
if (!mm_base_modem_at_command_full_finish (modem, res, NULL)) {
/* Try the next auth command */
ctx->auth_idx++;
authenticate (ctx);
return;
}
/* Store which auth command worked, for next attempts */
ctx->self->priv->auth_idx = ctx->auth_idx;
/* Success, activate the PDP context and start the data session */
command = g_strdup_printf ("AT_OWANCALL=%d,1,1",
ctx->cid);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
command,
3,
FALSE,
NULL, /* cancellable */
(GAsyncReadyCallback)activate_ready,
ctx);
g_free (command);
}
const gchar *auth_commands[] = {
"$QCPDPP",
/* Icera-based devices (GI0322/Quicksilver, iCON 505) don't implement
* $QCPDPP, but instead use _OPDPP with the same arguments.
*/
"_OPDPP",
NULL
};
static void
authenticate (Dial3gppContext *ctx)
{
gchar *command;
if (!auth_commands[ctx->auth_idx]) {
g_simple_async_result_set_error (ctx->result,
MM_CORE_ERROR,
MM_CORE_ERROR_FAILED,
"Couldn't run HSO authentication");
dial_3gpp_context_complete_and_free (ctx);
return;
}
/* Both user and password are required; otherwise firmware returns an error */
if (!ctx->self->priv->user || !ctx->self->priv->password)
command = g_strdup_printf ("%s=%d,0",
auth_commands[ctx->auth_idx],
ctx->cid);
else
command = g_strdup_printf ("%s=%d,1,\"%s\",\"%s\"",
auth_commands[ctx->auth_idx],
ctx->cid,
ctx->self->priv->password,
ctx->self->priv->user);
mm_base_modem_at_command_full (ctx->modem,
ctx->primary,
command,
3,
FALSE,
NULL, /* cancellable */
(GAsyncReadyCallback)authenticate_ready,
ctx);
g_free (command);
}
static void
dial_3gpp (MMBroadbandBearer *self,
MMBaseModem *modem,
MMAtSerialPort *primary,
guint cid,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_assert (primary != NULL);
authenticate (dial_3gpp_context_new (MM_BROADBAND_BEARER_HSO (self),
modem,
primary,
cid,
cancellable,
callback,
user_data));
}
/*****************************************************************************/
static gboolean
@@ -199,6 +564,7 @@ mm_broadband_bearer_hso_class_init (MMBroadbandBearerHsoClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
MMBearerClass *bearer_class = MM_BEARER_CLASS (klass);
MMBroadbandBearerClass *broadband_bearer_class = MM_BROADBAND_BEARER_CLASS (klass);
object_class->get_property = get_property;
object_class->set_property = set_property;
@@ -207,6 +573,9 @@ mm_broadband_bearer_hso_class_init (MMBroadbandBearerHsoClass *klass)
bearer_class->cmp_properties = cmp_properties;
bearer_class->expose_properties = expose_properties;
broadband_bearer_class->dial_3gpp = dial_3gpp;
broadband_bearer_class->dial_3gpp_finish = dial_3gpp_finish;
properties[PROP_USER] =
g_param_spec_string (MM_BROADBAND_BEARER_HSO_USER,
"User",
+10
View File
@@ -34,6 +34,13 @@
#define MM_BROADBAND_BEARER_HSO_USER "broadband-bearer-hso-user"
#define MM_BROADBAND_BEARER_HSO_PASSWORD "broadband-bearer-hso-password"
typedef enum {
MM_BROADBAND_BEARER_HSO_CONNECTION_STATUS_UNKNOWN,
MM_BROADBAND_BEARER_HSO_CONNECTION_STATUS_CONNECTED,
MM_BROADBAND_BEARER_HSO_CONNECTION_STATUS_CONNECTION_FAILED,
MM_BROADBAND_BEARER_HSO_CONNECTION_STATUS_DISCONNECTED
} MMBroadbandBearerHsoConnectionStatus;
typedef struct _MMBroadbandBearerHso MMBroadbandBearerHso;
typedef struct _MMBroadbandBearerHsoClass MMBroadbandBearerHsoClass;
typedef struct _MMBroadbandBearerHsoPrivate MMBroadbandBearerHsoPrivate;
@@ -58,4 +65,7 @@ void mm_broadband_bearer_hso_new (MMBroadbandModemHso *modem,
MMBearer *mm_broadband_bearer_hso_new_finish (GAsyncResult *res,
GError **error);
void mm_broadband_bearer_hso_report_connection_status (MMBroadbandBearerHso *self,
MMBroadbandBearerHsoConnectionStatus status);
#endif /* MM_BROADBAND_BEARER_HSO_H */