summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAleksander Morgado <aleksander@aleksander.es>2014-02-18 10:35:58 +0100
committerAleksander Morgado <aleksander@aleksander.es>2014-02-20 11:15:03 +0100
commit9239fbcd7a137f2bb349592d649fc80847e6e731 (patch)
tree3cae28c4c7027b7190c39b7a0d64c0850797f2ed
parent705dd0133d8a02c0bf2a2cfa3d8920030dc1392d (diff)
iface-modem: allow transitioning to the OFF power state
There are systems in which the modem power source can be controlled externally, e.g. to switch it on or off. In these cases, it is sometimes advisable to be able to tell the modem to cleanly shut off completely before the power is cut. So, allow transitioning to the OFF power state if the modem supports it, even if afterwards the modem could be completely unreachable.
-rw-r--r--src/mm-iface-modem.c88
-rw-r--r--src/mm-iface-modem.h7
2 files changed, 86 insertions, 9 deletions
diff --git a/src/mm-iface-modem.c b/src/mm-iface-modem.c
index 5edbbae0..9228bebf 100644
--- a/src/mm-iface-modem.c
+++ b/src/mm-iface-modem.c
@@ -1703,12 +1703,28 @@ handle_set_power_state_auth_ready (MMBaseModem *self,
return;
}
- /* Error if we're not in disabled state */
+ /* Only 'off', 'low' or 'up' expected */
+ if (ctx->power_state != MM_MODEM_POWER_STATE_LOW &&
+ ctx->power_state != MM_MODEM_POWER_STATE_ON &&
+ ctx->power_state != MM_MODEM_POWER_STATE_OFF) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_INVALID_ARGS,
+ "Cannot set '%s' power state",
+ mm_modem_power_state_get_string (ctx->power_state));
+ handle_set_power_state_context_free (ctx);
+ return;
+ }
+
modem_state = MM_MODEM_STATE_UNKNOWN;
g_object_get (self,
MM_IFACE_MODEM_STATE, &modem_state,
NULL);
- if (modem_state != MM_MODEM_STATE_DISABLED) {
+
+ /* Going into LOW or ON only allowed in disabled state */
+ if ((ctx->power_state == MM_MODEM_POWER_STATE_LOW ||
+ ctx->power_state == MM_MODEM_POWER_STATE_ON) &&
+ modem_state != MM_MODEM_STATE_DISABLED) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_WRONG_STATE,
@@ -1717,14 +1733,15 @@ handle_set_power_state_auth_ready (MMBaseModem *self,
return;
}
- /* Only 'low' or 'up' expected */
- if (ctx->power_state != MM_MODEM_POWER_STATE_LOW &&
- ctx->power_state != MM_MODEM_POWER_STATE_ON) {
+ /* Going into OFF, only allowed if locked, disabled or failed */
+ if (ctx->power_state == MM_MODEM_POWER_STATE_OFF &&
+ modem_state != MM_MODEM_STATE_FAILED &&
+ modem_state != MM_MODEM_STATE_LOCKED &&
+ modem_state != MM_MODEM_STATE_DISABLED) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
- MM_CORE_ERROR_INVALID_ARGS,
- "Cannot set '%s' power state",
- mm_modem_power_state_get_string (ctx->power_state));
+ MM_CORE_ERROR_WRONG_STATE,
+ "Cannot set power state: modem either enabled or initializing");
handle_set_power_state_context_free (ctx);
return;
}
@@ -3191,6 +3208,28 @@ modem_power_down_ready (MMIfaceModem *self,
}
static void
+modem_power_off_ready (MMIfaceModem *self,
+ GAsyncResult *res,
+ SetPowerStateContext *ctx)
+{
+ GError *error = NULL;
+
+ MM_IFACE_MODEM_GET_INTERFACE (self)->modem_power_off_finish (self, res, &error);
+ if (error) {
+ /* If the real and cached ones are different, set the real one */
+ if (ctx->previous_cached_power_state != ctx->previous_real_power_state)
+ mm_gdbus_modem_set_power_state (ctx->skeleton, ctx->previous_real_power_state);
+ g_simple_async_result_take_error (ctx->result, error);
+ } else {
+ mm_info ("Modem powered off... may no longer be accessible");
+ 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);
+}
+
+static void
set_power_state (SetPowerStateContext *ctx)
{
/* Already done if we're in the desired power state */
@@ -3205,11 +3244,42 @@ set_power_state (SetPowerStateContext *ctx)
return;
}
+ /* Don't allow trying to recover from a power off */
+ if (ctx->previous_real_power_state == MM_MODEM_POWER_STATE_OFF) {
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_WRONG_STATE,
+ "Cannot recover from a power off");
+ set_power_state_context_complete_and_free (ctx);
+ return;
+ }
+
/* Supported transitions:
- * UNKNOWN|OFF|LOW --> ON
+ * UNKNOWN|LOW --> ON
* ON --> LOW
+ * ON|LOW --> OFF
*/
+ /* Fully powering off the modem? */
+ if (ctx->power_state == MM_MODEM_POWER_STATE_OFF) {
+ /* Error if unsupported */
+ if (!MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_off ||
+ !MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_off_finish) {
+ g_simple_async_result_set_error (ctx->result,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Powering off is not supported by this modem");
+ set_power_state_context_complete_and_free (ctx);
+ return;
+ }
+
+ MM_IFACE_MODEM_GET_INTERFACE (ctx->self)->modem_power_off (
+ MM_IFACE_MODEM (ctx->self),
+ (GAsyncReadyCallback)modem_power_off_ready,
+ ctx);
+ return;
+ }
+
/* Going into low power mode? */
if (ctx->power_state == MM_MODEM_POWER_STATE_LOW) {
/* Error if unsupported */
diff --git a/src/mm-iface-modem.h b/src/mm-iface-modem.h
index 9b6db73d..265b965b 100644
--- a/src/mm-iface-modem.h
+++ b/src/mm-iface-modem.h
@@ -304,6 +304,13 @@ struct _MMIfaceModem {
GAsyncResult *res,
GError **error);
+ /* Asynchronous modem power-off operation */
+ void (*modem_power_off) (MMIfaceModem *self,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (*modem_power_off_finish) (MMIfaceModem *self,
+ GAsyncResult *res,
+ GError **error);
/* Create SIM */
void (*create_sim) (MMIfaceModem *self,
GAsyncReadyCallback callback,