diff options
author | Aleksander Morgado <aleksander@lanedo.com> | 2012-12-31 16:25:06 +0100 |
---|---|---|
committer | Aleksander Morgado <aleksander@lanedo.com> | 2013-01-11 10:05:45 +0100 |
commit | e55b543d384296ad07fddc3bbcb0a9ec51c86e6a (patch) | |
tree | da08e28626ae5e694939e13715a806fbbe2e26e9 /src | |
parent | 49aecb57dc2aeebb1e133816aec05321e5073cda (diff) |
iface-modem: implement power mode loading and setting
Diffstat (limited to 'src')
-rw-r--r-- | src/mm-iface-modem.c | 435 | ||||
-rw-r--r-- | src/mm-iface-modem.h | 17 |
2 files changed, 349 insertions, 103 deletions
diff --git a/src/mm-iface-modem.c b/src/mm-iface-modem.c index 9b5f8dcd..be4b70d6 100644 --- a/src/mm-iface-modem.c +++ b/src/mm-iface-modem.c @@ -1443,6 +1443,106 @@ typedef struct { MmGdbusModem *skeleton; GDBusMethodInvocation *invocation; MMIfaceModem *self; + MMModemPowerState power_state; +} HandleSetPowerStateContext; + +static void +handle_set_power_state_context_free (HandleSetPowerStateContext *ctx) +{ + g_object_unref (ctx->skeleton); + g_object_unref (ctx->invocation); + g_object_unref (ctx->self); + g_slice_free (HandleSetPowerStateContext, ctx); +} + +static void +set_power_state_ready (MMIfaceModem *self, + GAsyncResult *res, + HandleSetPowerStateContext *ctx) +{ + GError *error = NULL; + + if (!mm_iface_modem_set_power_state_finish (self, res, &error)) + g_dbus_method_invocation_take_error (ctx->invocation, error); + else + mm_gdbus_modem_complete_set_power_state (ctx->skeleton, ctx->invocation); + handle_set_power_state_context_free (ctx); +} + +static void +handle_set_power_state_auth_ready (MMBaseModem *self, + GAsyncResult *res, + HandleSetPowerStateContext *ctx) +{ + MMModemState modem_state; + GError *error = NULL; + + if (!mm_base_modem_authorize_finish (self, res, &error)) { + g_dbus_method_invocation_take_error (ctx->invocation, error); + handle_set_power_state_context_free (ctx); + return; + } + + /* Error if we're not in disabled state */ + modem_state = MM_MODEM_STATE_UNKNOWN; + g_object_get (self, + MM_IFACE_MODEM_STATE, &modem_state, + NULL); + if (modem_state != MM_MODEM_STATE_DISABLED) { + g_dbus_method_invocation_return_error (ctx->invocation, + MM_CORE_ERROR, + MM_CORE_ERROR_WRONG_STATE, + "Cannot set power state: not in disabled state"); + handle_set_power_state_context_free (ctx); + return; + } + + /* Only 'low' or 'up' expected */ + if (ctx->power_state != MM_MODEM_POWER_STATE_LOW && + ctx->power_state != MM_MODEM_POWER_STATE_ON) { + g_dbus_method_invocation_return_error (ctx->invocation, + MM_CORE_ERROR, + MM_CORE_ERROR_INVALID_ARGS, + "Cannot set '%s' power state", + ctx->power_state); + handle_set_power_state_context_free (ctx); + return; + } + + mm_iface_modem_set_power_state (MM_IFACE_MODEM (self), + ctx->power_state, + (GAsyncReadyCallback)set_power_state_ready, + ctx); +} + +static gboolean +handle_set_power_state (MmGdbusModem *skeleton, + GDBusMethodInvocation *invocation, + guint32 power_state, + MMIfaceModem *self) +{ + HandleSetPowerStateContext *ctx; + + ctx = g_slice_new (HandleSetPowerStateContext); + ctx->skeleton = g_object_ref (skeleton); + ctx->invocation = g_object_ref (invocation); + ctx->self = g_object_ref (self); + ctx->power_state = (MMModemPowerState)power_state; + + mm_base_modem_authorize (MM_BASE_MODEM (self), + invocation, + MM_AUTHORIZATION_DEVICE_CONTROL, + (GAsyncReadyCallback)handle_set_power_state_auth_ready, + ctx); + return TRUE; +} + +/*****************************************************************************/ + +typedef struct { + MmGdbusModem *skeleton; + GDBusMethodInvocation *invocation; + MMIfaceModem *self; } HandleResetContext; static void @@ -2592,6 +2692,185 @@ mm_iface_modem_update_lock_info (MMIfaceModem *self, } /*****************************************************************************/ +/* Set power state sequence */ + +typedef struct { + MMIfaceModem *self; + GSimpleAsyncResult *result; + MmGdbusModem *skeleton; + MMModemPowerState power_state; + MMModemPowerState previous_power_state; +} SetPowerStateContext; + +static void +set_power_state_context_complete_and_free (SetPowerStateContext *ctx) +{ + g_simple_async_result_complete_in_idle (ctx->result); + g_object_unref (ctx->self); + g_object_unref (ctx->result); + if (ctx->skeleton) + g_object_unref (ctx->skeleton); + g_slice_free (SetPowerStateContext, ctx); +} + +gboolean +mm_iface_modem_set_power_state_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + return !g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (res), error); +} + +static void +modem_after_power_up_ready (MMIfaceModem *self, + GAsyncResult *res, + SetPowerStateContext *ctx) +{ + GError *error = NULL; + + MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_power_up_finish (self, res, &error); + if (error) + g_simple_async_result_take_error (ctx->result, error); + else + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + set_power_state_context_complete_and_free (ctx); +} + +static void +modem_power_up_ready (MMIfaceModem *self, + GAsyncResult *res, + SetPowerStateContext *ctx) +{ + GError *error = NULL; + + MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up_finish (self, res, &error); + if (error) { + g_simple_async_result_take_error (ctx->result, error); + set_power_state_context_complete_and_free (ctx); + return; + } + + mm_dbg ("Modem set in full-power mode..."); + mm_gdbus_modem_set_power_state (ctx->skeleton, ctx->power_state); + + /* If we have something to do just after power-up, do it */ + if (MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_power_up && + MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_power_up_finish) { + MM_IFACE_MODEM_GET_INTERFACE (self)->modem_after_power_up ( + self, + (GAsyncReadyCallback)modem_after_power_up_ready, + ctx); + return; + } + + /* Otherwise, we're done */ + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + set_power_state_context_complete_and_free (ctx); +} + +static void +modem_power_down_ready (MMIfaceModem *self, + GAsyncResult *res, + SetPowerStateContext *ctx) +{ + GError *error = NULL; + + MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down_finish (self, res, &error); + if (error) + g_simple_async_result_take_error (ctx->result, error); + else { + mm_dbg ("Modem set in low-power mode..."); + mm_gdbus_modem_set_power_state (ctx->skeleton, ctx->power_state); + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + } + + set_power_state_context_complete_and_free (ctx); +} + +void +mm_iface_modem_set_power_state (MMIfaceModem *self, + MMModemPowerState power_state, + GAsyncReadyCallback callback, + gpointer user_data) +{ + SetPowerStateContext *ctx; + + ctx = g_slice_new0 (SetPowerStateContext); + ctx->self = g_object_ref (self); + ctx->result = g_simple_async_result_new (G_OBJECT (self), + callback, + user_data, + mm_iface_modem_set_power_state); + ctx->power_state = power_state; + g_object_get (ctx->self, + MM_IFACE_MODEM_DBUS_SKELETON, &ctx->skeleton, + NULL); + if (!ctx->skeleton) { + g_simple_async_result_set_error (ctx->result, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't get interface skeleton"); + set_power_state_context_complete_and_free (ctx); + return; + } + ctx->previous_power_state = mm_gdbus_modem_get_power_state (ctx->skeleton); + + /* Already done if we're in the desired power state */ + if (ctx->previous_power_state == ctx->power_state) { + mm_dbg ("No need to change power state: already in '%s' power state", + mm_modem_power_state_get_string (ctx->power_state)); + g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); + set_power_state_context_complete_and_free (ctx); + return; + } + + /* Supported transitions: + * UNKNOWN|OFF|LOW --> ON + * ON --> LOW + */ + + /* Going into low power mode? */ + if (ctx->power_state == MM_MODEM_POWER_STATE_LOW) { + /* Error if unsupported */ + if (!MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down || + !MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down_finish) { + g_simple_async_result_set_error (ctx->result, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "Going into low-power mode is not supported by this modem"); + set_power_state_context_complete_and_free (ctx); + return; + } + + MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down (MM_IFACE_MODEM (self), + (GAsyncReadyCallback)modem_power_down_ready, + ctx); + return; + } + + /* Going out of low power mode? */ + if (ctx->power_state == MM_MODEM_POWER_STATE_ON) { + /* Error if unsupported */ + if (!MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up || + !MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up_finish) { + g_simple_async_result_set_error (ctx->result, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "Going into full-power mode is not supported by this modem"); + set_power_state_context_complete_and_free (ctx); + return; + } + + MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_up (MM_IFACE_MODEM (self), + (GAsyncReadyCallback)modem_power_up_ready, + ctx); + return; + } + + g_assert_not_reached (); +} + +/*****************************************************************************/ /* MODEM DISABLING */ typedef struct _DisablingContext DisablingContext; @@ -2601,7 +2880,6 @@ typedef enum { DISABLING_STEP_FIRST, DISABLING_STEP_CURRENT_BANDS, DISABLING_STEP_ALLOWED_MODES, - DISABLING_STEP_MODEM_POWER_DOWN, DISABLING_STEP_LAST } DisablingStep; @@ -2633,27 +2911,6 @@ mm_iface_modem_disable_finish (MMIfaceModem *self, } static void -modem_power_down_ready (MMIfaceModem *self, - GAsyncResult *res, - DisablingContext *ctx) -{ - GError *error = NULL; - - MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_down_finish (self, res, &error); - if (error) { - g_simple_async_result_take_error (ctx->result, error); - disabling_context_complete_and_free (ctx); - return; - } - - mm_dbg ("Modem properly powered down..."); - - /* Go on to next step */ - ctx->step++; - interface_disabling_step (ctx); -} - -static void interface_disabling_step (DisablingContext *ctx) { switch (ctx->step) { @@ -2674,25 +2931,6 @@ interface_disabling_step (DisablingContext *ctx) /* Fall down to next step */ ctx->step++; - case DISABLING_STEP_MODEM_POWER_DOWN: - /* CFUN=0 is dangerous and often will shoot devices in the head (that's - * what it's supposed to do). So don't use CFUN=0 by default, but let - * specific plugins use it when they know it's safe to do so. For - * example, CFUN=0 will often make phones turn themselves off, but some - * dedicated devices (ex Sierra WWAN cards) will just turn off their - * radio but otherwise still work. - */ - if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_down && - MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_down_finish) { - MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_down ( - ctx->self, - (GAsyncReadyCallback)modem_power_down_ready, - ctx); - return; - } - /* Fall down to next step */ - ctx->step++; - case DISABLING_STEP_LAST: /* We are done without errors! */ g_simple_async_result_set_op_res_gboolean (ctx->result, TRUE); @@ -2742,8 +2980,7 @@ static void interface_enabling_step (EnablingContext *ctx); typedef enum { ENABLING_STEP_FIRST, ENABLING_STEP_MODEM_INIT, - ENABLING_STEP_MODEM_POWER_UP, - ENABLING_STEP_MODEM_AFTER_POWER_UP, + ENABLING_STEP_SET_POWER_STATE, ENABLING_STEP_FLOW_CONTROL, ENABLING_STEP_SUPPORTED_CHARSETS, ENABLING_STEP_CHARSET, @@ -2818,8 +3055,25 @@ mm_iface_modem_enable_finish (MMIfaceModem *self, } VOID_REPLY_READY_FN (modem_init); -VOID_REPLY_READY_FN (modem_power_up); -VOID_REPLY_READY_FN (modem_after_power_up); + +static void +enabling_set_power_state_ready (MMIfaceModem *self, + GAsyncResult *res, + EnablingContext *ctx) +{ + GError *error = NULL; + + if (!mm_iface_modem_set_power_state_finish (self, res, &error)) { + g_simple_async_result_take_error (ctx->result, error); + enabling_context_complete_and_free (ctx); + return; + } + + /* Go on to next step */ + ctx->step++; + interface_enabling_step (ctx); +} + VOID_REPLY_READY_FN (setup_flow_control); static void @@ -2965,29 +3219,12 @@ interface_enabling_step (EnablingContext *ctx) /* Fall down to next step */ ctx->step++; - case ENABLING_STEP_MODEM_POWER_UP: - if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_up && - MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_up_finish) { - MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_up ( - ctx->self, - (GAsyncReadyCallback)modem_power_up_ready, - ctx); - return; - } - /* Fall down to next step */ - ctx->step++; - - case ENABLING_STEP_MODEM_AFTER_POWER_UP: - if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_after_power_up && - MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_after_power_up_finish) { - MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_after_power_up ( - ctx->self, - (GAsyncReadyCallback)modem_after_power_up_ready, - ctx); - return; - } - /* Fall down to next step */ - ctx->step++; + case ENABLING_STEP_SET_POWER_STATE: + mm_iface_modem_set_power_state (ctx->self, + MM_MODEM_POWER_STATE_ON, + (GAsyncReadyCallback)enabling_set_power_state_ready, + ctx); + return; case ENABLING_STEP_FLOW_CONTROL: if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->setup_flow_control && @@ -3136,7 +3373,6 @@ typedef enum { INITIALIZATION_STEP_FIRST, INITIALIZATION_STEP_CURRENT_CAPABILITIES, INITIALIZATION_STEP_MODEM_CAPABILITIES, - INITIALIZATION_STEP_POWER_DOWN, INITIALIZATION_STEP_BEARERS, INITIALIZATION_STEP_MANUFACTURER, INITIALIZATION_STEP_MODEL, @@ -3148,6 +3384,7 @@ typedef enum { INITIALIZATION_STEP_OWN_NUMBERS, INITIALIZATION_STEP_SUPPORTED_MODES, INITIALIZATION_STEP_SUPPORTED_BANDS, + INITIALIZATION_STEP_POWER_STATE, INITIALIZATION_STEP_LAST } InitializationStep; @@ -3245,26 +3482,6 @@ initialization_context_complete_and_free_if_cancelled (InitializationContext *ct UINT_REPLY_READY_FN (current_capabilities, "Current Capabilities", TRUE) UINT_REPLY_READY_FN (modem_capabilities, "Modem Capabilities", FALSE) - -static void -initialization_modem_power_down_ready (MMIfaceModem *self, - GAsyncResult *res, - InitializationContext *ctx) -{ - GError *error = NULL; - - MM_IFACE_MODEM_GET_INTERFACE (self)->modem_init_power_down_finish (self, res, &error); - if (error) { - mm_dbg ("Couldn't power down the modem during initialization: '%s'", error->message); - g_error_free (error); - } else - mm_dbg ("Modem initially powered down..."); - - /* Go on to next step */ - ctx->step++; - interface_initialization_step (ctx); -} - STR_REPLY_READY_FN (manufacturer, "Manufacturer") STR_REPLY_READY_FN (model, "Model") STR_REPLY_READY_FN (revision, "Revision") @@ -3346,6 +3563,8 @@ load_supported_bands_ready (MMIfaceModem *self, interface_initialization_step (ctx); } +UINT_REPLY_READY_FN (power_state, "Power State", FALSE) + static void modem_update_lock_info_ready (MMIfaceModem *self, GAsyncResult *res, @@ -3512,20 +3731,6 @@ interface_initialization_step (InitializationContext *ctx) /* Fall down to next step */ ctx->step++; - case INITIALIZATION_STEP_POWER_DOWN: - /* We run the power down command during initialization, to ensure we - * start with radio off, when possible */ - if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_init_power_down && - MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_init_power_down_finish) { - MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_init_power_down ( - ctx->self, - (GAsyncReadyCallback)initialization_modem_power_down_ready, - ctx); - return; - } - /* Fall down to next step */ - ctx->step++; - case INITIALIZATION_STEP_BEARERS: { MMBearerList *list = NULL; @@ -3749,6 +3954,25 @@ interface_initialization_step (InitializationContext *ctx) ctx->step++; } + case INITIALIZATION_STEP_POWER_STATE: + /* Initial power state is meant to be loaded only once. Therefore, if we + * already have it loaded, don't try to load it again. */ + if (mm_gdbus_modem_get_power_state (ctx->skeleton) == MM_MODEM_POWER_STATE_UNKNOWN) { + if (MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_power_state && + MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_power_state_finish) { + MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->load_power_state ( + ctx->self, + (GAsyncReadyCallback)load_power_state_ready, + ctx); + return; + } + + /* We don't know how to load current power state; assume ON */ + mm_gdbus_modem_set_power_state (ctx->skeleton, MM_MODEM_POWER_STATE_ON); + } + /* Fall down to next step */ + ctx->step++; + case INITIALIZATION_STEP_LAST: if (ctx->fatal_error) { g_simple_async_result_take_error (ctx->result, ctx->fatal_error); @@ -3777,6 +4001,10 @@ interface_initialization_step (InitializationContext *ctx) G_CALLBACK (handle_enable), ctx->self); g_signal_connect (ctx->skeleton, + "handle-set-power-state", + G_CALLBACK (handle_set_power_state), + ctx->self); + g_signal_connect (ctx->skeleton, "handle-reset", G_CALLBACK (handle_reset), ctx->self); @@ -3855,6 +4083,7 @@ mm_iface_modem_initialize (MMIfaceModem *self, mm_gdbus_modem_set_preferred_mode (skeleton, MM_MODEM_MODE_NONE); mm_gdbus_modem_set_supported_bands (skeleton, mm_common_build_bands_unknown ()); mm_gdbus_modem_set_bands (skeleton, mm_common_build_bands_unknown ()); + mm_gdbus_modem_set_power_state (skeleton, MM_MODEM_POWER_STATE_UNKNOWN); /* Bind our State property */ g_object_bind_property (self, MM_IFACE_MODEM_STATE, diff --git a/src/mm-iface-modem.h b/src/mm-iface-modem.h index 23dac75b..6ba2b3fb 100644 --- a/src/mm-iface-modem.h +++ b/src/mm-iface-modem.h @@ -164,6 +164,14 @@ struct _MMIfaceModem { GAsyncResult *res, GError **error); + /* Loading of the PowerState property */ + void (* load_power_state) (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data); + MMModemPowerState (*load_power_state_finish) (MMIfaceModem *self, + GAsyncResult *res, + GError **error); + /* Loading of the SignalQuality property */ void (*load_signal_quality) (MMIfaceModem *self, GAsyncReadyCallback callback, @@ -362,6 +370,15 @@ gboolean mm_iface_modem_disable_finish (MMIfaceModem *self, GAsyncResult *res, GError **error); +/* Allow setting power state */ +void mm_iface_modem_set_power_state (MMIfaceModem *self, + MMModemPowerState power_state, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mm_iface_modem_set_power_state_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error); + /* Shutdown Modem interface */ void mm_iface_modem_shutdown (MMIfaceModem *self); |