aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@lanedo.com>2012-10-09 18:59:56 +0200
committerAleksander Morgado <aleksander@lanedo.com>2013-03-22 08:12:59 +0100
commit3b66047d941e8d38c38022fc835ce1f5dab32bf8 (patch)
tree6e4a2a615ed0b954c76d6e022691f62f1e4d72c4
parent6252df0bef49c417a4d5b7bab51695d57de3b658 (diff)
broadband-modem-qmi: implement automatic CDMA activation
-rw-r--r--src/mm-broadband-modem-qmi.c347
1 files changed, 344 insertions, 3 deletions
diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c
index b8892078..6d7efa2d 100644
--- a/src/mm-broadband-modem-qmi.c
+++ b/src/mm-broadband-modem-qmi.c
@@ -86,6 +86,11 @@ struct _MMBroadbandModemQmiPrivate {
guint system_info_indication_id;
#endif /* WITH_NEWEST_QMI_COMMANDS */
+ /* CDMA activation helpers */
+ MMModemCdmaActivationState activation_state;
+ guint activation_event_report_indication_id;
+ gpointer activation_ctx;
+
/* Messaging helpers */
gboolean messaging_unsolicited_events_enabled;
gboolean messaging_unsolicited_events_setup;
@@ -4618,14 +4623,20 @@ load_activation_state_context_complete_and_free (LoadActivationStateContext *ctx
}
static MMModemCdmaActivationState
-modem_cdma_load_activation_state_finish (MMIfaceModemCdma *self,
+modem_cdma_load_activation_state_finish (MMIfaceModemCdma *_self,
GAsyncResult *res,
GError **error)
{
+ MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
+
if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error))
return MM_MODEM_CDMA_ACTIVATION_STATE_UNKNOWN;
- return (MMModemCdmaActivationState) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+ /* Cache the value and also return it */
+ self->priv->activation_state =
+ (MMModemCdmaActivationState) GPOINTER_TO_UINT (g_simple_async_result_get_op_res_gpointer (G_SIMPLE_ASYNC_RESULT (res)));
+
+ return self->priv->activation_state;
}
static void
@@ -4693,7 +4704,335 @@ modem_cdma_load_activation_state (MMIfaceModemCdma *self,
ctx);
}
-/* /\*****************************************************************************\/ */
+/*****************************************************************************/
+/* OTA activation (CDMA interface) */
+
+typedef enum {
+ CDMA_ACTIVATION_STEP_FIRST,
+ CDMA_ACTIVATION_STEP_ENABLE_INDICATIONS,
+ CDMA_ACTIVATION_STEP_REQUEST_ACTIVATION,
+ CDMA_ACTIVATION_STEP_WAIT_UNTIL_FINISHED,
+ CDMA_ACTIVATION_STEP_POWER_CYCLE,
+ CDMA_ACTIVATION_STEP_LAST
+} CdmaActivationStep;
+
+typedef struct {
+ MMBroadbandModemQmi *self;
+ QmiClientDms *client;
+ GSimpleAsyncResult *result;
+ CdmaActivationStep step;
+ gchar *carrier_code;
+} CdmaActivationContext;
+
+static void
+cdma_activation_context_complete_and_free (CdmaActivationContext *ctx)
+{
+ /* Cleanup the activation context from the private info */
+ ctx->self->priv->activation_ctx = NULL;
+
+ g_simple_async_result_complete (ctx->result);
+ g_object_unref (ctx->result);
+ g_object_unref (ctx->client);
+ g_object_unref (ctx->self);
+ g_free (ctx->carrier_code);
+ g_slice_free (CdmaActivationContext, ctx);
+}
+
+static gboolean
+modem_cdma_activate_finish (MMIfaceModemCdma *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error);
+}
+
+static void cdma_activation_context_step (CdmaActivationContext *ctx);
+
+static void
+cdma_activation_disable_indications (CdmaActivationContext *ctx)
+{
+ QmiMessageDmsSetEventReportInput *input;
+
+ /* Remove the signal handler */
+ g_assert (ctx->self->priv->activation_event_report_indication_id != 0);
+ g_signal_handler_disconnect (ctx->client, ctx->self->priv->activation_event_report_indication_id);
+ ctx->self->priv->activation_event_report_indication_id = 0;
+
+ /* Disable the activation state change indications; don't worry about the result */
+ input = qmi_message_dms_set_event_report_input_new ();
+ qmi_message_dms_set_event_report_input_set_activation_state_reporting (input, FALSE, NULL);
+ qmi_client_dms_set_event_report (ctx->client, input, 5, NULL, NULL, NULL);
+ qmi_message_dms_set_event_report_input_unref (input);
+}
+
+static void
+activation_power_cycle_ready (MMBroadbandModemQmi *self,
+ GAsyncResult *res,
+ CdmaActivationContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!power_cycle_finish (self, res, &error)) {
+ g_simple_async_result_take_error (ctx->result, error);
+ cdma_activation_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* And go on to next step */
+ ctx->step++;
+ cdma_activation_context_step (ctx);
+}
+
+static void
+activation_event_report_indication_cb (QmiClientDms *client,
+ QmiIndicationDmsEventReportOutput *output,
+ MMBroadbandModemQmi *self)
+{
+ QmiDmsActivationState state;
+ MMModemCdmaActivationState new;
+ GError *error;
+
+ /* If the indication doesn't have any activation state info, just return */
+ if (!qmi_indication_dms_event_report_output_get_activation_state (output, &state, NULL))
+ return;
+
+ mm_dbg ("Activation state update: '%s'",
+ qmi_dms_activation_state_get_string (state));
+
+ new = mm_modem_cdma_activation_state_from_qmi_activation_state (state);
+
+ if (self->priv->activation_state != new)
+ mm_info ("Activation state changed: '%s'-->'%s'",
+ mm_modem_cdma_activation_state_get_string (self->priv->activation_state),
+ mm_modem_cdma_activation_state_get_string (new));
+
+ /* Cache the new value */
+ self->priv->activation_state = new;
+
+ /* We consider a not-activated report in the indication as a failure */
+ error = (new == MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED ?
+ g_error_new (MM_CDMA_ACTIVATION_ERROR,
+ MM_CDMA_ACTIVATION_ERROR_UNKNOWN,
+ "Activation process failed") :
+ NULL);
+
+ /* Update activation state in the interface */
+ mm_iface_modem_cdma_update_activation_state (MM_IFACE_MODEM_CDMA (self), new, error);
+
+ /* Now, if we have a FINAL state, finish the ongoing activation state request */
+ if (new != MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATING) {
+ CdmaActivationContext *ctx;
+
+ g_assert (self->priv->activation_ctx != NULL);
+ ctx = (CdmaActivationContext *)self->priv->activation_ctx;
+
+ /* Disable further indications. */
+ cdma_activation_disable_indications (ctx);
+
+ /* If there is any error, finish the async method */
+ if (error) {
+ g_simple_async_result_take_error (ctx->result, error);
+ cdma_activation_context_complete_and_free (ctx);
+ return;
+ }
+
+ /* Otherwise, go on to next step */
+ ctx->step++;
+ cdma_activation_context_step (ctx);
+ return;
+ }
+
+ mm_dbg ("Activation process still ongoing...");
+}
+
+static void
+activate_automatic_ready (QmiClientDms *client,
+ GAsyncResult *res,
+ CdmaActivationContext *ctx)
+{
+ QmiMessageDmsActivateAutomaticOutput *output;
+ GError *error = NULL;
+
+ output = qmi_client_dms_activate_automatic_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_simple_async_result_take_error (ctx->result, error);
+ cdma_activation_disable_indications (ctx);
+ cdma_activation_context_complete_and_free (ctx);
+ return;
+ }
+
+ if (!qmi_message_dms_activate_automatic_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't request OTA activation: ");
+ g_simple_async_result_take_error (ctx->result, error);
+ qmi_message_dms_activate_automatic_output_unref (output);
+ cdma_activation_disable_indications (ctx);
+ cdma_activation_context_complete_and_free (ctx);
+ return;
+ }
+
+ qmi_message_dms_activate_automatic_output_unref (output);
+
+ /* Keep on */
+ ctx->step++;
+ cdma_activation_context_step (ctx);
+}
+
+static void
+ser_activation_state_ready (QmiClientDms *client,
+ GAsyncResult *res,
+ CdmaActivationContext *ctx)
+{
+ QmiMessageDmsSetEventReportOutput *output;
+ GError *error = NULL;
+
+ /* We cannot ignore errors, we NEED the indications to finish the
+ * activation request properly */
+
+ output = qmi_client_dms_set_event_report_finish (client, res, &error);
+ if (!output) {
+ g_prefix_error (&error, "QMI operation failed: ");
+ g_simple_async_result_take_error (ctx->result, error);
+ cdma_activation_context_complete_and_free (ctx);
+ return;
+ }
+
+ if (!qmi_message_dms_set_event_report_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't set event report: ");
+ g_simple_async_result_take_error (ctx->result, error);
+ qmi_message_dms_set_event_report_output_unref (output);
+ cdma_activation_context_complete_and_free (ctx);
+ return;
+ }
+
+ qmi_message_dms_set_event_report_output_unref (output);
+
+ /* Setup the indication handler */
+ g_assert (ctx->self->priv->activation_event_report_indication_id == 0);
+ ctx->self->priv->activation_event_report_indication_id =
+ g_signal_connect (client,
+ "event-report",
+ G_CALLBACK (activation_event_report_indication_cb),
+ ctx->self);
+
+ /* Keep on */
+ ctx->step++;
+ cdma_activation_context_step (ctx);
+}
+
+static void
+cdma_activation_context_step (CdmaActivationContext *ctx)
+{
+ switch (ctx->step) {
+ case CDMA_ACTIVATION_STEP_FIRST:
+ ctx->step++;
+ /* Fall down to next step */
+
+ case CDMA_ACTIVATION_STEP_ENABLE_INDICATIONS: {
+ QmiMessageDmsSetEventReportInput *input;
+
+ mm_info ("Automatic activation step [1/5]: enabling indications");
+
+ input = qmi_message_dms_set_event_report_input_new ();
+ qmi_message_dms_set_event_report_input_set_activation_state_reporting (input, TRUE, NULL);
+ qmi_client_dms_set_event_report (
+ ctx->client,
+ input,
+ 5,
+ NULL,
+ (GAsyncReadyCallback)ser_activation_state_ready,
+ ctx);
+ qmi_message_dms_set_event_report_input_unref (input);
+ return;
+ }
+
+ case CDMA_ACTIVATION_STEP_REQUEST_ACTIVATION: {
+ QmiMessageDmsActivateAutomaticInput *input;
+
+ mm_info ("Automatic activation step [2/5]: requesting activation");
+
+ input = qmi_message_dms_activate_automatic_input_new ();
+ qmi_message_dms_activate_automatic_input_set_activation_code (input, ctx->carrier_code, NULL);
+ qmi_client_dms_activate_automatic (ctx->client,
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback)activate_automatic_ready,
+ ctx);
+ qmi_message_dms_activate_automatic_input_unref (input);
+ return;
+ }
+
+ case CDMA_ACTIVATION_STEP_WAIT_UNTIL_FINISHED:
+ mm_info ("Automatic activation step [3/5]: waiting for activation state updates");
+ return;
+
+ case CDMA_ACTIVATION_STEP_POWER_CYCLE:
+ mm_info ("Automatic activation step [4/5]: power-cycling...");
+ power_cycle (ctx->self,
+ (GAsyncReadyCallback)activation_power_cycle_ready,
+ ctx);
+ return;
+
+ case CDMA_ACTIVATION_STEP_LAST:
+ mm_info ("Automatic activation step [5/5]: finished");
+ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE);
+ cdma_activation_context_complete_and_free (ctx);
+ return;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
+static void
+modem_cdma_activate (MMIfaceModemCdma *_self,
+ const gchar *carrier_code,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self);
+ GSimpleAsyncResult *result;
+ CdmaActivationContext *ctx;
+ QmiClient *client = NULL;
+
+ if (!ensure_qmi_client (MM_BROADBAND_MODEM_QMI (self),
+ QMI_SERVICE_DMS, &client,
+ callback, user_data))
+ return;
+
+ result = g_simple_async_result_new (G_OBJECT (self),
+ callback,
+ user_data,
+ modem_cdma_activate);
+
+ /* Fail if we have already an activation ongoing */
+ if (self->priv->activation_ctx) {
+ g_simple_async_result_set_error (
+ result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_IN_PROGRESS,
+ "An activation operation is already in progress");
+ g_simple_async_result_complete_in_idle (result);
+ g_object_unref (result);
+ return;
+ }
+
+ /* Setup context */
+ ctx = g_slice_new0 (CdmaActivationContext);
+ ctx->self = g_object_ref (self);
+ ctx->client = g_object_ref (client);
+ ctx->result = result;
+ ctx->carrier_code = g_strdup (carrier_code);
+ ctx->step = CDMA_ACTIVATION_STEP_FIRST;
+
+ /* We keep the activation context in the private data, so that we don't
+ * allow multiple activation requests at the same time. */
+ self->priv->activation_ctx = ctx;
+ cdma_activation_context_step (ctx);
+}
+
+/*****************************************************************************/
/* Setup/Cleanup unsolicited registration event handlers
* (3GPP and CDMA interface) */
@@ -7738,6 +8077,8 @@ iface_modem_cdma_init (MMIfaceModemCdma *iface)
iface->run_registration_checks_finish = modem_cdma_run_registration_checks_finish;
iface->load_activation_state = modem_cdma_load_activation_state;
iface->load_activation_state_finish = modem_cdma_load_activation_state_finish;
+ iface->activate = modem_cdma_activate;
+ iface->activate_finish = modem_cdma_activate_finish;
}
static void