ublox: implement current modes setting and modem power up/down/off/reset
Changing current allowed/preferred modes requires the device to be in low-power mode, so we will make sure we return an error if any power operation is already ongoing when a new one is requested.
This commit is contained in:
@@ -43,8 +43,277 @@ struct _MMBroadbandModemUbloxPrivate {
|
||||
/* Networking mode in use */
|
||||
MMUbloxNetworkingMode mode;
|
||||
gboolean mode_checked;
|
||||
|
||||
/* Flag to specify whether a power operation is ongoing */
|
||||
gboolean power_operation_ongoing;
|
||||
|
||||
/* Mode combination to apply if "any" requested */
|
||||
MMModemMode any_allowed;
|
||||
};
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static gboolean
|
||||
acquire_power_operation (MMBroadbandModemUblox *self,
|
||||
GError **error)
|
||||
{
|
||||
if (self->priv->power_operation_ongoing) {
|
||||
g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_RETRY,
|
||||
"An operation which requires power updates is currently in progress");
|
||||
return FALSE;
|
||||
}
|
||||
self->priv->power_operation_ongoing = TRUE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static void
|
||||
release_power_operation (MMBroadbandModemUblox *self)
|
||||
{
|
||||
g_assert (self->priv->power_operation_ongoing);
|
||||
self->priv->power_operation_ongoing = FALSE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Set allowed modes (Modem interface) */
|
||||
|
||||
typedef enum {
|
||||
SET_CURRENT_MODES_STEP_FIRST,
|
||||
SET_CURRENT_MODES_STEP_ACQUIRE,
|
||||
SET_CURRENT_MODES_STEP_CURRENT_POWER,
|
||||
SET_CURRENT_MODES_STEP_POWER_DOWN,
|
||||
SET_CURRENT_MODES_STEP_URAT,
|
||||
SET_CURRENT_MODES_STEP_RECOVER_CURRENT_POWER,
|
||||
SET_CURRENT_MODES_STEP_RELEASE,
|
||||
SET_CURRENT_MODES_STEP_LAST,
|
||||
} SetCurrentModesStep;
|
||||
|
||||
typedef struct {
|
||||
MMBroadbandModemUblox *self;
|
||||
SetCurrentModesStep step;
|
||||
gchar *command;
|
||||
MMModemPowerState initial_state;
|
||||
GError *saved_error;
|
||||
} SetCurrentModesContext;
|
||||
|
||||
static void
|
||||
set_current_modes_context_free (SetCurrentModesContext *ctx)
|
||||
{
|
||||
g_assert (!ctx->saved_error);
|
||||
g_free (ctx->command);
|
||||
g_object_unref (ctx->self);
|
||||
g_slice_free (SetCurrentModesContext, ctx);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
set_current_modes_finish (MMIfaceModem *self,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
static void set_current_modes_step (GTask *task);
|
||||
|
||||
static void
|
||||
set_current_modes_recover_power_ready (MMBaseModem *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
SetCurrentModesContext *ctx;
|
||||
|
||||
ctx = (SetCurrentModesContext *) g_task_get_task_data (task);
|
||||
g_assert (ctx);
|
||||
|
||||
/* propagate the error if none already set */
|
||||
mm_base_modem_at_command_finish (self, res, ctx->saved_error ? NULL : &ctx->saved_error);
|
||||
|
||||
/* Go to next step (release power operation) regardless of the result */
|
||||
ctx->step++;
|
||||
set_current_modes_step (task);
|
||||
}
|
||||
|
||||
static void
|
||||
set_current_modes_urat_ready (MMBaseModem *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
SetCurrentModesContext *ctx;
|
||||
|
||||
ctx = (SetCurrentModesContext *) g_task_get_task_data (task);
|
||||
g_assert (ctx);
|
||||
|
||||
mm_base_modem_at_command_finish (self, res, &ctx->saved_error);
|
||||
|
||||
/* Go to next step (recover current power) regardless of the result */
|
||||
ctx->step++;
|
||||
set_current_modes_step (task);
|
||||
}
|
||||
|
||||
static void
|
||||
set_current_modes_low_power_ready (MMBaseModem *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
SetCurrentModesContext *ctx;
|
||||
|
||||
ctx = (SetCurrentModesContext *) g_task_get_task_data (task);
|
||||
g_assert (ctx);
|
||||
|
||||
if (!mm_base_modem_at_command_finish (self, res, &ctx->saved_error))
|
||||
ctx->step = SET_CURRENT_MODES_STEP_RELEASE;
|
||||
else
|
||||
ctx->step++;
|
||||
|
||||
set_current_modes_step (task);
|
||||
}
|
||||
|
||||
static void
|
||||
set_current_modes_current_power_ready (MMBaseModem *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
SetCurrentModesContext *ctx;
|
||||
const gchar *response;
|
||||
|
||||
ctx = (SetCurrentModesContext *) g_task_get_task_data (task);
|
||||
g_assert (ctx);
|
||||
|
||||
response = mm_base_modem_at_command_finish (self, res, &ctx->saved_error);
|
||||
if (!response || !mm_ublox_parse_cfun_response (response, &ctx->initial_state, &ctx->saved_error))
|
||||
ctx->step = SET_CURRENT_MODES_STEP_RELEASE;
|
||||
else
|
||||
ctx->step++;
|
||||
|
||||
set_current_modes_step (task);
|
||||
}
|
||||
|
||||
static void
|
||||
set_current_modes_step (GTask *task)
|
||||
{
|
||||
SetCurrentModesContext *ctx;
|
||||
|
||||
ctx = (SetCurrentModesContext *) g_task_get_task_data (task);
|
||||
g_assert (ctx);
|
||||
|
||||
switch (ctx->step) {
|
||||
case SET_CURRENT_MODES_STEP_FIRST:
|
||||
ctx->step++;
|
||||
/* fall down */
|
||||
|
||||
case SET_CURRENT_MODES_STEP_ACQUIRE:
|
||||
mm_dbg ("acquiring power operation...");
|
||||
if (!acquire_power_operation (ctx->self, &ctx->saved_error)) {
|
||||
ctx->step = SET_CURRENT_MODES_STEP_LAST;
|
||||
set_current_modes_step (task);
|
||||
return;
|
||||
}
|
||||
ctx->step++;
|
||||
/* fall down */
|
||||
|
||||
case SET_CURRENT_MODES_STEP_CURRENT_POWER:
|
||||
mm_dbg ("checking current power operation...");
|
||||
mm_base_modem_at_command (MM_BASE_MODEM (ctx->self),
|
||||
"+CFUN?",
|
||||
3,
|
||||
FALSE,
|
||||
(GAsyncReadyCallback) set_current_modes_current_power_ready,
|
||||
task);
|
||||
return;
|
||||
|
||||
case SET_CURRENT_MODES_STEP_POWER_DOWN:
|
||||
if (ctx->initial_state != MM_MODEM_POWER_STATE_LOW) {
|
||||
mm_dbg ("powering down before AcT change...");
|
||||
mm_base_modem_at_command (
|
||||
MM_BASE_MODEM (ctx->self),
|
||||
"+CFUN=4",
|
||||
3,
|
||||
FALSE,
|
||||
(GAsyncReadyCallback) set_current_modes_low_power_ready,
|
||||
task);
|
||||
return;
|
||||
}
|
||||
ctx->step++;
|
||||
/* fall down */
|
||||
|
||||
case SET_CURRENT_MODES_STEP_URAT:
|
||||
mm_dbg ("updating AcT...");
|
||||
mm_base_modem_at_command (
|
||||
MM_BASE_MODEM (ctx->self),
|
||||
ctx->command,
|
||||
3,
|
||||
FALSE,
|
||||
(GAsyncReadyCallback) set_current_modes_urat_ready,
|
||||
task);
|
||||
return;
|
||||
|
||||
case SET_CURRENT_MODES_STEP_RECOVER_CURRENT_POWER:
|
||||
if (ctx->initial_state != MM_MODEM_POWER_STATE_LOW) {
|
||||
mm_dbg ("recovering power state after AcT change...");
|
||||
mm_base_modem_at_command (
|
||||
MM_BASE_MODEM (ctx->self),
|
||||
"+CFUN=1",
|
||||
3,
|
||||
FALSE,
|
||||
(GAsyncReadyCallback) set_current_modes_recover_power_ready,
|
||||
task);
|
||||
return;
|
||||
}
|
||||
ctx->step++;
|
||||
/* fall down */
|
||||
|
||||
case SET_CURRENT_MODES_STEP_RELEASE:
|
||||
mm_dbg ("releasing power operation...");
|
||||
release_power_operation (ctx->self);
|
||||
ctx->step++;
|
||||
/* fall down */
|
||||
|
||||
case SET_CURRENT_MODES_STEP_LAST:
|
||||
if (ctx->saved_error) {
|
||||
g_task_return_error (task, ctx->saved_error);
|
||||
ctx->saved_error = NULL;
|
||||
} else
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
set_current_modes (MMIfaceModem *self,
|
||||
MMModemMode allowed,
|
||||
MMModemMode preferred,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
SetCurrentModesContext *ctx;
|
||||
gchar *command;
|
||||
GError *error = NULL;
|
||||
|
||||
task = g_task_new (self, NULL, callback, user_data);
|
||||
|
||||
/* Handle ANY */
|
||||
if (allowed == MM_MODEM_MODE_ANY)
|
||||
allowed = MM_BROADBAND_MODEM_UBLOX (self)->priv->any_allowed;
|
||||
|
||||
/* Build command */
|
||||
command = mm_ublox_build_urat_set_command (allowed, preferred, &error);
|
||||
if (!command) {
|
||||
g_task_return_error (task, error);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx = g_slice_new0 (SetCurrentModesContext);
|
||||
ctx->self = MM_BROADBAND_MODEM_UBLOX (g_object_ref (self));
|
||||
ctx->command = command;
|
||||
ctx->initial_state = MM_MODEM_POWER_STATE_UNKNOWN;
|
||||
ctx->step = SET_CURRENT_MODES_STEP_FIRST;
|
||||
g_task_set_task_data (task, ctx, (GDestroyNotify) set_current_modes_context_free);
|
||||
|
||||
set_current_modes_step (task);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Load current modes (Modem interface) */
|
||||
|
||||
@@ -98,6 +367,9 @@ load_supported_modes_finish (MMIfaceModem *self,
|
||||
if (!(combinations = mm_ublox_filter_supported_modes (mm_iface_modem_get_model (self), combinations, error)))
|
||||
return FALSE;
|
||||
|
||||
/* Decide and store which combination to apply when ANY requested */
|
||||
MM_BROADBAND_MODEM_UBLOX (self)->priv->any_allowed = mm_ublox_get_modem_mode_any (combinations);
|
||||
|
||||
return combinations;
|
||||
}
|
||||
|
||||
@@ -145,6 +417,92 @@ load_power_state (MMIfaceModem *self,
|
||||
user_data);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Modem power up/down/off (Modem interface) */
|
||||
|
||||
static gboolean
|
||||
common_modem_power_operation_finish (MMIfaceModem *self,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
static void
|
||||
power_operation_ready (MMBaseModem *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
release_power_operation (MM_BROADBAND_MODEM_UBLOX (self));
|
||||
|
||||
if (!mm_base_modem_at_command_finish (self, res, &error))
|
||||
g_task_return_error (task, error);
|
||||
else
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
static void
|
||||
common_modem_power_operation (MMBroadbandModemUblox *self,
|
||||
const gchar *command,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
GError *error = NULL;
|
||||
|
||||
task = g_task_new (self, NULL, callback, user_data);
|
||||
|
||||
/* Fail if there is already an ongoing power management operation */
|
||||
if (!acquire_power_operation (self, &error)) {
|
||||
g_task_return_error (task, error);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Use AT+CFUN=4 for power down, puts device in airplane mode */
|
||||
mm_base_modem_at_command (MM_BASE_MODEM (self),
|
||||
command,
|
||||
30,
|
||||
FALSE,
|
||||
(GAsyncReadyCallback) power_operation_ready,
|
||||
task);
|
||||
}
|
||||
|
||||
static void
|
||||
modem_reset (MMIfaceModem *self,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
common_modem_power_operation (MM_BROADBAND_MODEM_UBLOX (self), "+CFUN=16", callback, user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
modem_power_off (MMIfaceModem *self,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
common_modem_power_operation (MM_BROADBAND_MODEM_UBLOX (self), "+CPWROFF", callback, user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
modem_power_down (MMIfaceModem *self,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
common_modem_power_operation (MM_BROADBAND_MODEM_UBLOX (self), "+CFUN=4", callback, user_data);
|
||||
}
|
||||
|
||||
static void
|
||||
modem_power_up (MMIfaceModem *self,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
common_modem_power_operation (MM_BROADBAND_MODEM_UBLOX (self), "+CFUN=1", callback, user_data);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Create Bearer (Modem interface) */
|
||||
|
||||
@@ -417,6 +775,7 @@ mm_broadband_modem_ublox_init (MMBroadbandModemUblox *self)
|
||||
MMBroadbandModemUbloxPrivate);
|
||||
self->priv->profile = MM_UBLOX_USB_PROFILE_UNKNOWN;
|
||||
self->priv->mode = MM_UBLOX_NETWORKING_MODE_UNKNOWN;
|
||||
self->priv->any_allowed = MM_MODEM_MODE_NONE;
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -426,10 +785,20 @@ iface_modem_init (MMIfaceModem *iface)
|
||||
iface->create_bearer_finish = modem_create_bearer_finish;
|
||||
iface->load_power_state = load_power_state;
|
||||
iface->load_power_state_finish = load_power_state_finish;
|
||||
iface->modem_power_up = modem_power_up;
|
||||
iface->modem_power_up_finish = common_modem_power_operation_finish;
|
||||
iface->modem_power_down = modem_power_down;
|
||||
iface->modem_power_down_finish = common_modem_power_operation_finish;
|
||||
iface->modem_power_off = modem_power_off;
|
||||
iface->modem_power_off_finish = common_modem_power_operation_finish;
|
||||
iface->reset = modem_reset;
|
||||
iface->reset_finish = common_modem_power_operation_finish;
|
||||
iface->load_supported_modes = load_supported_modes;
|
||||
iface->load_supported_modes_finish = load_supported_modes_finish;
|
||||
iface->load_current_modes = load_current_modes;
|
||||
iface->load_current_modes_finish = load_current_modes_finish;
|
||||
iface->set_current_modes = set_current_modes;
|
||||
iface->set_current_modes_finish = set_current_modes_finish;
|
||||
}
|
||||
|
||||
static void
|
||||
|
@@ -459,6 +459,37 @@ mm_ublox_filter_supported_modes (const gchar *model,
|
||||
return filtered;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Get mode to apply when ANY */
|
||||
|
||||
MMModemMode
|
||||
mm_ublox_get_modem_mode_any (const GArray *combinations)
|
||||
{
|
||||
guint i;
|
||||
MMModemMode any = MM_MODEM_MODE_NONE;
|
||||
guint any_bits_set = 0;
|
||||
|
||||
for (i = 0; i < combinations->len; i++) {
|
||||
MMModemModeCombination *combination;
|
||||
guint bits_set;
|
||||
|
||||
combination = &g_array_index (combinations, MMModemModeCombination, i);
|
||||
if (combination->preferred == MM_MODEM_MODE_NONE)
|
||||
continue;
|
||||
bits_set = mm_count_bits_set (combination->allowed);
|
||||
if (bits_set > any_bits_set) {
|
||||
any_bits_set = bits_set;
|
||||
any = combination->allowed;
|
||||
}
|
||||
}
|
||||
|
||||
/* If combinations were processed via mm_ublox_parse_urat_test_response(),
|
||||
* we're sure that there will be at least one combination with preferred
|
||||
* 'none', so there must be some valid combination as result */
|
||||
g_assert (any != MM_MODEM_MODE_NONE);
|
||||
return any;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* URAT? response parser */
|
||||
|
||||
|
@@ -78,6 +78,11 @@ GArray *mm_ublox_filter_supported_modes (const gchar *model,
|
||||
GArray *combinations,
|
||||
GError **error);
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Get mode to apply when ANY */
|
||||
|
||||
MMModemMode mm_ublox_get_modem_mode_any (const GArray *combinations);
|
||||
|
||||
/*****************************************************************************/
|
||||
/* URAT? response parser */
|
||||
|
||||
|
Reference in New Issue
Block a user