diff options
author | Dan Williams <dan@ioncontrol.co> | 2024-09-24 23:50:00 -0500 |
---|---|---|
committer | Dan Williams <dan@ioncontrol.co> | 2024-09-24 23:52:13 -0500 |
commit | 64646b98cd3aa3e7ba978783a28000c062cd788b (patch) | |
tree | e94b72164739205226622fd151a310e9194e1027 | |
parent | a1f8045c533729c8a200608052eb712dd41bd543 (diff) |
plugins/quectel: support modem power operations for AT-based devices
Signed-off-by: Dan Williams <dan@ioncontrol.co>
-rw-r--r-- | src/plugins/quectel/mm-broadband-modem-quectel.c | 184 | ||||
-rw-r--r-- | src/plugins/quectel/mm-broadband-modem-quectel.h | 4 |
2 files changed, 187 insertions, 1 deletions
diff --git a/src/plugins/quectel/mm-broadband-modem-quectel.c b/src/plugins/quectel/mm-broadband-modem-quectel.c index ff5a22e1..174727e7 100644 --- a/src/plugins/quectel/mm-broadband-modem-quectel.c +++ b/src/plugins/quectel/mm-broadband-modem-quectel.c @@ -11,6 +11,7 @@ * GNU General Public License for more details: * * Copyright (C) 2018-2020 Aleksander Morgado <aleksander@aleksander.es> + * Copyright (C) 2024 JUCR GmbH */ #include <config.h> @@ -21,6 +22,7 @@ #include "mm-iface-modem-location.h" #include "mm-iface-modem-time.h" #include "mm-shared-quectel.h" +#include "mm-base-modem-at.h" static void iface_modem_init (MMIfaceModemInterface *iface); static void iface_modem_firmware_init (MMIfaceModemFirmwareInterface *iface); @@ -38,6 +40,175 @@ G_DEFINE_TYPE_EXTENDED (MMBroadbandModemQuectel, mm_broadband_modem_quectel, MM_ G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init) G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_QUECTEL, shared_quectel_init)) +struct _MMBroadbandModemQuectelPrivate { + /* Flag to specify whether a power operation is ongoing */ + gboolean power_operation_ongoing; +}; + +/*****************************************************************************/ + +static gboolean +acquire_power_operation (MMBroadbandModemQuectel *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 (MMBroadbandModemQuectel *self) +{ + g_assert (self->priv->power_operation_ongoing); + self->priv->power_operation_ongoing = FALSE; +} + +/*****************************************************************************/ +/* Power state loading (Modem interface) */ + +static MMModemPowerState +load_power_state_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + MMModemPowerState state = MM_MODEM_POWER_STATE_UNKNOWN; + guint cfun_state = 0; + const gchar *response; + + response = mm_base_modem_at_command_finish (MM_BASE_MODEM (self), res, error); + if (!response) + return MM_MODEM_POWER_STATE_UNKNOWN; + + if (!mm_3gpp_parse_cfun_query_response (response, &cfun_state, error)) + return MM_MODEM_POWER_STATE_UNKNOWN; + + switch (cfun_state) { + case 0: + state = MM_MODEM_POWER_STATE_OFF; + break; + case 1: + state = MM_MODEM_POWER_STATE_ON; + break; + case 4: + state = MM_MODEM_POWER_STATE_LOW; + break; + /* Some modems support the following (ASR chipsets at least) */ + case 3: /* Disable RX */ + case 5: /* Disable (U)SIM */ + /* We'll just call these low-power... */ + state = MM_MODEM_POWER_STATE_LOW; + break; + default: + g_set_error (error, MM_CORE_ERROR, MM_CORE_ERROR_FAILED, + "Unknown +CFUN pòwer state: '%u'", state); + return MM_MODEM_POWER_STATE_UNKNOWN; + } + + return state; +} + +static void +load_power_state (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + mm_base_modem_at_command (MM_BASE_MODEM (self), + "+CFUN?", + 3, + FALSE, + callback, + 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_QUECTEL (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 (MMBroadbandModemQuectel *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; + } + + 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_QUECTEL (self), "+CFUN=1,1", callback, user_data); +} + +static void +modem_power_off (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + common_modem_power_operation (MM_BROADBAND_MODEM_QUECTEL (self), "+QPOWD=1", callback, user_data); +} + +static void +modem_power_down (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + common_modem_power_operation (MM_BROADBAND_MODEM_QUECTEL (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_QUECTEL (self), "+CFUN=1", callback, user_data); +} + /*****************************************************************************/ MMBroadbandModemQuectel * @@ -65,6 +236,9 @@ mm_broadband_modem_quectel_new (const gchar *device, static void mm_broadband_modem_quectel_init (MMBroadbandModemQuectel *self) { + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + MM_TYPE_BROADBAND_MODEM_QUECTEL, + MMBroadbandModemQuectelPrivate); } static void @@ -75,6 +249,16 @@ iface_modem_init (MMIfaceModemInterface *iface) iface->setup_sim_hot_swap = mm_shared_quectel_setup_sim_hot_swap; iface->setup_sim_hot_swap_finish = mm_shared_quectel_setup_sim_hot_swap_finish; iface->cleanup_sim_hot_swap = mm_shared_quectel_cleanup_sim_hot_swap; + 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; } static void diff --git a/src/plugins/quectel/mm-broadband-modem-quectel.h b/src/plugins/quectel/mm-broadband-modem-quectel.h index 2067ff10..468cd094 100644 --- a/src/plugins/quectel/mm-broadband-modem-quectel.h +++ b/src/plugins/quectel/mm-broadband-modem-quectel.h @@ -27,9 +27,11 @@ typedef struct _MMBroadbandModemQuectel MMBroadbandModemQuectel; typedef struct _MMBroadbandModemQuectelClass MMBroadbandModemQuectelClass; +typedef struct _MMBroadbandModemQuectelPrivate MMBroadbandModemQuectelPrivate; struct _MMBroadbandModemQuectel { - MMBroadbandModem parent; + MMBroadbandModem parent; + MMBroadbandModemQuectelPrivate *priv; }; struct _MMBroadbandModemQuectelClass{ |