diff options
author | Dan Williams <dan@ioncontrol.co> | 2025-04-18 08:57:49 -0500 |
---|---|---|
committer | Dan Williams <dan@ioncontrol.co> | 2025-05-08 20:24:37 -0500 |
commit | 9830e3955a3e45ff82c1c76bcba3b53432eaa51e (patch) | |
tree | 6141fb7aeee5422c56acd7733341a85e4d0c0ed2 | |
parent | 6d0e4daf877e0600966bbf9fb34dfba14b2ccb54 (diff) |
base-sms,sms-at: split AT-specific SMS code into MMSmsAt
Simplify MMBaseSms (making it easier to use from testcases) by
splitting the AT-specific code into MMSmsAt rather than keeping
it in the base class.
Signed-off-by: Dan Williams <dan@ioncontrol.co>
-rw-r--r-- | src/meson.build | 1 | ||||
-rw-r--r-- | src/mm-base-sms.c | 762 | ||||
-rw-r--r-- | src/mm-base-sms.h | 3 | ||||
-rw-r--r-- | src/mm-broadband-modem.c | 5 | ||||
-rw-r--r-- | src/mm-sms-at.c | 835 | ||||
-rw-r--r-- | src/mm-sms-at.h | 59 |
6 files changed, 898 insertions, 767 deletions
diff --git a/src/meson.build b/src/meson.build index c3242953..a9be07b0 100644 --- a/src/meson.build +++ b/src/meson.build @@ -298,6 +298,7 @@ sources = files( 'mm-base-modem.c', 'mm-base-sim.c', 'mm-base-sms.c', + 'mm-sms-at.c', 'mm-bind.c', 'mm-bearer-list.c', 'mm-broadband-bearer.c', diff --git a/src/mm-base-sms.c b/src/mm-base-sms.c index 1b022614..d6c93d0c 100644 --- a/src/mm-base-sms.c +++ b/src/mm-base-sms.c @@ -32,8 +32,6 @@ #include "mm-iface-modem.h" #include "mm-iface-modem-messaging.h" #include "mm-sms-part-3gpp.h" -#include "mm-base-modem-at.h" -#include "mm-base-modem.h" #include "mm-log-object.h" #include "mm-modem-helpers.h" #include "mm-error-helpers.h" @@ -781,749 +779,6 @@ mm_base_sms_get_parts (MMBaseSms *self) /*****************************************************************************/ -static gboolean -sms_get_store_or_send_command (MMBaseSms *self, - MMSmsPart *part, - gboolean text_or_pdu, /* TRUE for PDU */ - gboolean store_or_send, /* TRUE for send */ - gchar **out_cmd, - gchar **out_msg_data, - GError **error) -{ - g_assert (out_cmd != NULL); - g_assert (out_msg_data != NULL); - - if (!text_or_pdu) { - /* Text mode */ - *out_cmd = g_strdup_printf ("+CMG%c=\"%s\"", - store_or_send ? 'S' : 'W', - mm_sms_part_get_number (part)); - *out_msg_data = g_strdup_printf ("%s\x1a", mm_sms_part_get_text (part)); - } else { - g_autofree gchar *hex = NULL; - g_autofree guint8 *pdu = NULL; - guint pdulen = 0; - guint msgstart = 0; - - /* AT+CMGW=<length>[, <stat>]<CR> PDU can be entered. <CTRL-Z>/<ESC> */ - - pdu = mm_sms_part_3gpp_get_submit_pdu (part, &pdulen, &msgstart, self, error); - if (!pdu) - /* 'error' should already be set */ - return FALSE; - - /* Convert PDU to hex */ - hex = mm_utils_bin2hexstr (pdu, pdulen); - if (!hex) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Not enough memory to send SMS PDU"); - return FALSE; - } - - /* CMGW/S length is the size of the PDU without SMSC information */ - *out_cmd = g_strdup_printf ("+CMG%c=%d", - store_or_send ? 'S' : 'W', - pdulen - msgstart); - *out_msg_data = g_strdup_printf ("%s\x1a", hex); - } - - return TRUE; -} - -/*****************************************************************************/ -/* Store the SMS */ - -typedef struct { - MMBaseModem *modem; - MMIfacePortAt *port; - MMSmsStorage storage; - gboolean need_unlock; - gboolean use_pdu_mode; - GList *current; - gchar *msg_data; -} SmsStoreContext; - -static void -sms_store_context_free (SmsStoreContext *ctx) -{ - /* Unlock mem2 storage if we had the lock */ - if (ctx->need_unlock) { - mm_iface_modem_messaging_unlock_storages (MM_IFACE_MODEM_MESSAGING (ctx->modem), - FALSE, - TRUE); - } - g_object_unref (ctx->port); - g_object_unref (ctx->modem); - g_free (ctx->msg_data); - g_slice_free (SmsStoreContext, ctx); -} - -static gboolean -sms_store_finish (MMBaseSms *self, - GAsyncResult *res, - GError **error) -{ - return g_task_propagate_boolean (G_TASK (res), error); -} - -static void sms_store_next_part (GTask *task); - -static void -store_msg_data_ready (MMBaseModem *modem, - GAsyncResult *res, - GTask *task) -{ - SmsStoreContext *ctx; - const gchar *response; - GError *error = NULL; - gint rv; - gint idx; - - response = mm_base_modem_at_command_full_finish (modem, res, &error); - if (error) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - /* Read the new part index from the reply */ - rv = sscanf (response, "+CMGW: %d", &idx); - if (rv != 1 || idx < 0) { - g_task_return_new_error (task, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't read index of already stored part: " - "%d fields parsed", - rv); - g_object_unref (task); - return; - } - - ctx = g_task_get_task_data (task); - - /* Set the index in the part we hold */ - mm_sms_part_set_index ((MMSmsPart *)ctx->current->data, (guint)idx); - - ctx->current = g_list_next (ctx->current); - sms_store_next_part (task); -} - -static void -store_ready (MMBaseModem *modem, - GAsyncResult *res, - GTask *task) -{ - SmsStoreContext *ctx; - GError *error = NULL; - - mm_base_modem_at_command_full_finish (modem, res, &error); - if (error) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - ctx = g_task_get_task_data (task); - - /* Send the actual message data. - * We send the data as 'raw' data because we do NOT want it to - * be treated as an AT command (i.e. we don't want it prefixed - * with AT+ and suffixed with <CR><LF>), plus, we want it to be - * sent right away (not queued after other AT commands). */ - mm_base_modem_at_command_full (ctx->modem, - ctx->port, - ctx->msg_data, - 10, - FALSE, - TRUE, /* raw */ - NULL, - (GAsyncReadyCallback)store_msg_data_ready, - task); -} - -static void -sms_store_next_part (GTask *task) -{ - MMBaseSms *self; - SmsStoreContext *ctx; - GError *error = NULL; - g_autofree gchar *cmd = NULL; - - self = g_task_get_source_object (task); - ctx = g_task_get_task_data (task); - - if (!ctx->current) { - /* Done we are */ - g_task_return_boolean (task, TRUE); - g_object_unref (task); - return; - } - - g_clear_pointer (&ctx->msg_data, g_free); - - if (!sms_get_store_or_send_command (self, - (MMSmsPart *)ctx->current->data, - ctx->use_pdu_mode, - FALSE, - &cmd, - &ctx->msg_data, - &error)) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - g_assert (cmd != NULL); - g_assert (ctx->msg_data != NULL); - - mm_base_modem_at_command_full (ctx->modem, - ctx->port, - cmd, - 10, - FALSE, - FALSE, /* raw */ - NULL, - (GAsyncReadyCallback)store_ready, - task); -} - -static void -store_lock_sms_storages_ready (MMIfaceModemMessaging *messaging, - GAsyncResult *res, - GTask *task) -{ - MMBaseSms *self; - SmsStoreContext *ctx; - GError *error = NULL; - - if (!mm_iface_modem_messaging_lock_storages_finish (messaging, res, &error)) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - self = g_task_get_source_object (task); - ctx = g_task_get_task_data (task); - - /* We are now locked. Whatever result we have here, we need to make sure - * we unlock the storages before finishing. */ - ctx->need_unlock = TRUE; - - /* Go on to store the parts */ - ctx->current = self->priv->parts; - sms_store_next_part (task); -} - -static void -sms_store (MMBaseSms *self, - MMSmsStorage storage, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SmsStoreContext *ctx; - GTask *task; - MMIfacePortAt *port; - GError *error = NULL; - - task = g_task_new (self, NULL, callback, user_data); - - /* Select port for the operation */ - port = mm_base_modem_peek_best_at_port (self->priv->modem, &error); - if (!port) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - /* Setup the context */ - ctx = g_slice_new0 (SmsStoreContext); - ctx->modem = g_object_ref (self->priv->modem); - ctx->port = g_object_ref (port); - ctx->storage = storage; - - /* Different ways to do it if on PDU or text mode */ - g_assert (MM_IS_IFACE_MODEM_MESSAGING (self->priv->modem)); - g_object_get (self->priv->modem, - MM_IFACE_MODEM_MESSAGING_SMS_PDU_MODE, &ctx->use_pdu_mode, - NULL); - g_task_set_task_data (task, ctx, (GDestroyNotify)sms_store_context_free); - - /* First, lock storage to use */ - mm_iface_modem_messaging_lock_storages ( - MM_IFACE_MODEM_MESSAGING (self->priv->modem), - MM_SMS_STORAGE_UNKNOWN, /* none required for mem1 */ - ctx->storage, - (GAsyncReadyCallback)store_lock_sms_storages_ready, - task); -} - -/*****************************************************************************/ -/* Send the SMS */ - -typedef struct { - MMBaseModem *modem; - MMIfacePortAt *port; - gboolean need_unlock; - gboolean from_storage; - gboolean use_pdu_mode; - GList *current; - gchar *msg_data; -} SmsSendContext; - -static void -sms_send_context_free (SmsSendContext *ctx) -{ - /* Unlock mem2 storage if we had the lock */ - if (ctx->need_unlock) { - mm_iface_modem_messaging_unlock_storages (MM_IFACE_MODEM_MESSAGING (ctx->modem), - FALSE, - TRUE); - } - g_object_unref (ctx->port); - g_object_unref (ctx->modem); - g_free (ctx->msg_data); - g_slice_free (SmsSendContext, ctx); -} - -static gboolean -sms_send_finish (MMBaseSms *self, - GAsyncResult *res, - GError **error) -{ - return g_task_propagate_boolean (G_TASK (res), error); -} - -static void sms_send_next_part (GTask *task); - -static gint -read_message_reference_from_reply (const gchar *response, - GError **error) -{ - gint rv = 0; - gint idx = -1; - - if (strstr (response, "+CMGS")) - rv = sscanf (strstr (response, "+CMGS"), "+CMGS: %d", &idx); - else if (strstr (response, "+CMSS")) - rv = sscanf (strstr (response, "+CMSS"), "+CMSS: %d", &idx); - - if (rv != 1 || idx < 0) { - g_set_error (error, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't read message reference: " - "%d fields parsed from response '%s'", - rv, response); - return -1; - } - - return idx; -} - -static void -send_generic_msg_data_ready (MMBaseModem *modem, - GAsyncResult *res, - GTask *task) -{ - SmsSendContext *ctx; - GError *error = NULL; - const gchar *response; - gint message_reference; - - response = mm_base_modem_at_command_full_finish (modem, res, &error); - if (error) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - message_reference = read_message_reference_from_reply (response, &error); - if (error) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - ctx = g_task_get_task_data (task); - - mm_sms_part_set_message_reference ((MMSmsPart *)ctx->current->data, - (guint)message_reference); - - ctx->current = g_list_next (ctx->current); - sms_send_next_part (task); -} - -static void -send_generic_ready (MMBaseModem *modem, - GAsyncResult *res, - GTask *task) -{ - SmsSendContext *ctx; - GError *error = NULL; - - mm_base_modem_at_command_full_finish (modem, res, &error); - if (error) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - ctx = g_task_get_task_data (task); - - /* Send the actual message data. - * We send the data as 'raw' data because we do NOT want it to - * be treated as an AT command (i.e. we don't want it prefixed - * with AT+ and suffixed with <CR><LF>), plus, we want it to be - * sent right away (not queued after other AT commands). */ - mm_base_modem_at_command_full (ctx->modem, - ctx->port, - ctx->msg_data, - MM_BASE_SMS_DEFAULT_SEND_TIMEOUT, - FALSE, - TRUE, /* raw */ - NULL, - (GAsyncReadyCallback)send_generic_msg_data_ready, - task); -} - -static void -send_from_storage_ready (MMBaseModem *modem, - GAsyncResult *res, - GTask *task) -{ - MMBaseSms *self; - SmsSendContext *ctx; - GError *error = NULL; - const gchar *response; - gint message_reference; - - self = g_task_get_source_object (task); - ctx = g_task_get_task_data (task); - - response = mm_base_modem_at_command_full_finish (modem, res, &error); - if (error) { - if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - mm_obj_dbg (self, "couldn't send SMS from storage: %s; trying generic send...", error->message); - g_error_free (error); - - ctx->from_storage = FALSE; - sms_send_next_part (task); - return; - } - - message_reference = read_message_reference_from_reply (response, &error); - if (error) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - mm_sms_part_set_message_reference ((MMSmsPart *)ctx->current->data, - (guint)message_reference); - - ctx->current = g_list_next (ctx->current); - sms_send_next_part (task); -} - -static void -sms_send_next_part (GTask *task) -{ - MMBaseSms *self; - SmsSendContext *ctx; - GError *error = NULL; - g_autofree gchar *cmd = NULL; - - self = g_task_get_source_object (task); - ctx = g_task_get_task_data (task); - - if (!ctx->current) { - /* Done we are */ - g_task_return_boolean (task, TRUE); - g_object_unref (task); - return; - } - - /* Send from storage */ - if (ctx->from_storage) { - cmd = g_strdup_printf ("+CMSS=%d", mm_sms_part_get_index ((MMSmsPart *)ctx->current->data)); - mm_base_modem_at_command_full (ctx->modem, - ctx->port, - cmd, - MM_BASE_SMS_DEFAULT_SEND_TIMEOUT, - FALSE, - FALSE, - NULL, - (GAsyncReadyCallback)send_from_storage_ready, - task); - return; - } - - /* Generic send */ - - g_clear_pointer (&ctx->msg_data, g_free); - - if (!sms_get_store_or_send_command (self, - (MMSmsPart *)ctx->current->data, - ctx->use_pdu_mode, - TRUE, - &cmd, - &ctx->msg_data, - &error)) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - g_assert (cmd != NULL); - g_assert (ctx->msg_data != NULL); - - /* no network involved in this initial AT command, so lower timeout */ - mm_base_modem_at_command_full (ctx->modem, - ctx->port, - cmd, - 10, - FALSE, - FALSE, /* raw */ - NULL, - (GAsyncReadyCallback)send_generic_ready, - task); -} - -static void -send_lock_sms_storages_ready (MMIfaceModemMessaging *messaging, - GAsyncResult *res, - GTask *task) -{ - MMBaseSms *self; - SmsSendContext *ctx; - GError *error = NULL; - - if (!mm_iface_modem_messaging_lock_storages_finish (messaging, res, &error)) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - self = g_task_get_source_object (task); - ctx = g_task_get_task_data (task); - - /* We are now locked. Whatever result we have here, we need to make sure - * we unlock the storages before finishing. */ - ctx->need_unlock = TRUE; - - /* Go on to send the parts */ - ctx->current = self->priv->parts; - sms_send_next_part (task); -} - -static void -sms_send (MMBaseSms *self, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SmsSendContext *ctx; - GTask *task; - MMIfacePortAt *port; - GError *error = NULL; - - task = g_task_new (self, NULL, callback, user_data); - - /* Select port for the operation */ - port = mm_base_modem_peek_best_at_port (self->priv->modem, &error); - if (!port) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - /* Setup the context */ - ctx = g_slice_new0 (SmsSendContext); - ctx->modem = g_object_ref (self->priv->modem); - ctx->port = g_object_ref (port); - g_task_set_task_data (task, ctx, (GDestroyNotify)sms_send_context_free); - - /* If the SMS is STORED, try to send from storage */ - ctx->from_storage = (mm_base_sms_get_storage (self) != MM_SMS_STORAGE_UNKNOWN); - if (ctx->from_storage) { - /* When sending from storage, first lock storage to use */ - g_assert (MM_IS_IFACE_MODEM_MESSAGING (self->priv->modem)); - mm_iface_modem_messaging_lock_storages ( - MM_IFACE_MODEM_MESSAGING (self->priv->modem), - MM_SMS_STORAGE_UNKNOWN, /* none required for mem1 */ - mm_base_sms_get_storage (self), - (GAsyncReadyCallback)send_lock_sms_storages_ready, - task); - return; - } - - /* Different ways to do it if on PDU or text mode */ - g_object_get (self->priv->modem, - MM_IFACE_MODEM_MESSAGING_SMS_PDU_MODE, &ctx->use_pdu_mode, - NULL); - ctx->current = self->priv->parts; - sms_send_next_part (task); -} - -/*****************************************************************************/ - -typedef struct { - MMBaseModem *modem; - gboolean need_unlock; - GList *current; - guint n_failed; -} SmsDeletePartsContext; - -static void -sms_delete_parts_context_free (SmsDeletePartsContext *ctx) -{ - /* Unlock mem1 storage if we had the lock */ - if (ctx->need_unlock) { - mm_iface_modem_messaging_unlock_storages (MM_IFACE_MODEM_MESSAGING (ctx->modem), - TRUE, - FALSE); - } - g_object_unref (ctx->modem); - g_slice_free (SmsDeletePartsContext, ctx); -} - -static gboolean -sms_delete_finish (MMBaseSms *self, - GAsyncResult *res, - GError **error) -{ - return g_task_propagate_boolean (G_TASK (res), error); -} - -static void delete_next_part (GTask *task); - -static void -delete_part_ready (MMBaseModem *modem, - GAsyncResult *res, - GTask *task) -{ - MMBaseSms *self; - SmsDeletePartsContext *ctx; - g_autoptr(GError) error = NULL; - - self = g_task_get_source_object (task); - ctx = g_task_get_task_data (task); - - mm_base_modem_at_command_finish (modem, res, &error); - if (error) { - ctx->n_failed++; - mm_obj_dbg (self, "couldn't delete SMS part with index %u: %s", - mm_sms_part_get_index ((MMSmsPart *)ctx->current->data), - error->message); - } - - /* We reset the index, as there is no longer that part */ - mm_sms_part_set_index ((MMSmsPart *)ctx->current->data, SMS_PART_INVALID_INDEX); - - ctx->current = g_list_next (ctx->current); - delete_next_part (task); -} - -static void -delete_next_part (GTask *task) -{ - SmsDeletePartsContext *ctx; - g_autofree gchar *cmd = NULL; - - ctx = g_task_get_task_data (task); - - /* Skip non-stored parts */ - while (ctx->current && (mm_sms_part_get_index ((MMSmsPart *)ctx->current->data) == SMS_PART_INVALID_INDEX)) - ctx->current = g_list_next (ctx->current); - - /* If all removed, we're done */ - if (!ctx->current) { - if (ctx->n_failed > 0) - g_task_return_new_error (task, - MM_CORE_ERROR, - MM_CORE_ERROR_FAILED, - "Couldn't delete %u parts from this SMS", - ctx->n_failed); - else - g_task_return_boolean (task, TRUE); - g_object_unref (task); - return; - } - - cmd = g_strdup_printf ("+CMGD=%d", mm_sms_part_get_index ((MMSmsPart *)ctx->current->data)); - mm_base_modem_at_command (ctx->modem, - cmd, - 10, - FALSE, - (GAsyncReadyCallback)delete_part_ready, - task); -} - -static void -delete_lock_sms_storages_ready (MMIfaceModemMessaging *messaging, - GAsyncResult *res, - GTask *task) -{ - MMBaseSms *self; - SmsDeletePartsContext *ctx; - GError *error = NULL; - - if (!mm_iface_modem_messaging_lock_storages_finish (messaging, res, &error)) { - g_task_return_error (task, error); - g_object_unref (task); - return; - } - - self = g_task_get_source_object (task); - ctx = g_task_get_task_data (task); - - /* We are now locked. Whatever result we have here, we need to make sure - * we unlock the storages before finishing. */ - ctx->need_unlock = TRUE; - - /* Go on deleting parts */ - ctx->current = self->priv->parts; - delete_next_part (task); -} - -static void -sms_delete (MMBaseSms *self, - GAsyncReadyCallback callback, - gpointer user_data) -{ - SmsDeletePartsContext *ctx; - GTask *task; - - ctx = g_slice_new0 (SmsDeletePartsContext); - ctx->modem = g_object_ref (self->priv->modem); - - task = g_task_new (self, NULL, callback, user_data); - g_task_set_task_data (task, ctx, (GDestroyNotify)sms_delete_parts_context_free); - - if (mm_base_sms_get_storage (self) == MM_SMS_STORAGE_UNKNOWN) { - mm_obj_dbg (self, "not removing parts from non-stored SMS"); - g_task_return_boolean (task, TRUE); - g_object_unref (task); - return; - } - - /* Select specific storage to delete from */ - mm_iface_modem_messaging_lock_storages ( - MM_IFACE_MODEM_MESSAGING (self->priv->modem), - mm_base_sms_get_storage (self), - MM_SMS_STORAGE_UNKNOWN, /* none required for mem2 */ - (GAsyncReadyCallback)delete_lock_sms_storages_ready, - task); -} - -/*****************************************************************************/ - gboolean mm_base_sms_delete_finish (MMBaseSms *self, GAsyncResult *res, @@ -1797,16 +1052,6 @@ mm_base_sms_multipart_take_part (MMBaseSms *self, } MMBaseSms * -mm_base_sms_new (MMBaseModem *modem, gboolean is_3gpp) -{ - return MM_BASE_SMS (g_object_new (MM_TYPE_BASE_SMS, - MM_BASE_SMS_MODEM, modem, - MM_BIND_TO, modem, - MM_BASE_SMS_IS_3GPP, is_3gpp, - NULL)); -} - -MMBaseSms * mm_base_sms_singlepart_new (MMBaseModem *modem, MMSmsState state, MMSmsStorage storage, @@ -2139,13 +1384,6 @@ mm_base_sms_class_init (MMBaseSmsClass *klass) object_class->finalize = finalize; object_class->dispose = dispose; - klass->store = sms_store; - klass->store_finish = sms_store_finish; - klass->send = sms_send; - klass->send_finish = sms_send_finish; - klass->delete = sms_delete; - klass->delete_finish = sms_delete_finish; - properties[PROP_CONNECTION] = g_param_spec_object (MM_BASE_SMS_CONNECTION, "Connection", diff --git a/src/mm-base-sms.h b/src/mm-base-sms.h index ffcaf8c4..5fceb719 100644 --- a/src/mm-base-sms.h +++ b/src/mm-base-sms.h @@ -91,9 +91,6 @@ struct _MMBaseSmsClass { GType mm_base_sms_get_type (void); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMBaseSms, g_object_unref) -/* This one can be overridden by plugins */ -MMBaseSms *mm_base_sms_new (MMBaseModem *modem, - gboolean is_3gpp); MMBaseSms *mm_base_sms_new_from_properties (MMBaseModem *modem, MMSmsProperties *properties, GError **error); diff --git a/src/mm-broadband-modem.c b/src/mm-broadband-modem.c index dbcc61a9..cb01676d 100644 --- a/src/mm-broadband-modem.c +++ b/src/mm-broadband-modem.c @@ -52,6 +52,7 @@ #include "mm-cbm-part.h" #include "mm-sms-list.h" #include "mm-sms-part-3gpp.h" +#include "mm-sms-at.h" #include "mm-call-list.h" #include "mm-base-sim.h" #include "mm-log-object.h" @@ -8228,8 +8229,8 @@ modem_messaging_load_initial_sms_parts (MMIfaceModemMessaging *self, static MMBaseSms * modem_messaging_create_sms (MMIfaceModemMessaging *self) { - return mm_base_sms_new (MM_BASE_MODEM (self), - mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))); + return mm_sms_at_new (MM_BASE_MODEM (self), + mm_iface_modem_is_3gpp (MM_IFACE_MODEM (self))); } /*****************************************************************************/ diff --git a/src/mm-sms-at.c b/src/mm-sms-at.c new file mode 100644 index 00000000..3fc7c155 --- /dev/null +++ b/src/mm-sms-at.c @@ -0,0 +1,835 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Copyright (C) 2008 - 2009 Novell, Inc. + * Copyright (C) 2009 - 2012 Red Hat, Inc. + * Copyright (C) 2012 Google, Inc. + * Copyright (C) 2025 Dan Williams <dan@ioncontrol.co> + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <ctype.h> + +#include <ModemManager.h> +#define _LIBMM_INSIDE_MM +#include <libmm-glib.h> + +#include "mm-sms-at.h" +#include "mm-iface-modem-messaging.h" +#include "mm-sms-part-3gpp.h" +#include "mm-base-modem-at.h" +#include "mm-log-object.h" + +G_DEFINE_TYPE (MMSmsAt, mm_sms_at, MM_TYPE_BASE_SMS) + +struct _MMSmsAtPrivate { + MMBaseModem *modem; +}; + +/*****************************************************************************/ + +static gboolean +sms_get_store_or_send_command (MMSmsAt *self, + MMSmsPart *part, + gboolean text_or_pdu, /* TRUE for PDU */ + gboolean store_or_send, /* TRUE for send */ + gchar **out_cmd, + gchar **out_msg_data, + GError **error) +{ + g_assert (out_cmd != NULL); + g_assert (out_msg_data != NULL); + + if (!text_or_pdu) { + /* Text mode */ + *out_cmd = g_strdup_printf ("+CMG%c=\"%s\"", + store_or_send ? 'S' : 'W', + mm_sms_part_get_number (part)); + *out_msg_data = g_strdup_printf ("%s\x1a", mm_sms_part_get_text (part)); + } else { + g_autofree gchar *hex = NULL; + g_autofree guint8 *pdu = NULL; + guint pdulen = 0; + guint msgstart = 0; + + /* AT+CMGW=<length>[, <stat>]<CR> PDU can be entered. <CTRL-Z>/<ESC> */ + + pdu = mm_sms_part_3gpp_get_submit_pdu (part, &pdulen, &msgstart, self, error); + if (!pdu) + /* 'error' should already be set */ + return FALSE; + + /* Convert PDU to hex */ + hex = mm_utils_bin2hexstr (pdu, pdulen); + if (!hex) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Not enough memory to send SMS PDU"); + return FALSE; + } + + /* CMGW/S length is the size of the PDU without SMSC information */ + *out_cmd = g_strdup_printf ("+CMG%c=%d", + store_or_send ? 'S' : 'W', + pdulen - msgstart); + *out_msg_data = g_strdup_printf ("%s\x1a", hex); + } + + return TRUE; +} + +/*****************************************************************************/ +/* Store the SMS */ + +typedef struct { + MMBaseModem *modem; + MMIfacePortAt *port; + MMSmsStorage storage; + gboolean need_unlock; + gboolean use_pdu_mode; + GList *current; + gchar *msg_data; +} SmsStoreContext; + +static void +sms_store_context_free (SmsStoreContext *ctx) +{ + /* Unlock mem2 storage if we had the lock */ + if (ctx->need_unlock) { + mm_iface_modem_messaging_unlock_storages (MM_IFACE_MODEM_MESSAGING (ctx->modem), + FALSE, + TRUE); + } + g_object_unref (ctx->port); + g_object_unref (ctx->modem); + g_free (ctx->msg_data); + g_slice_free (SmsStoreContext, ctx); +} + +static gboolean +sms_store_finish (MMBaseSms *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void sms_store_next_part (GTask *task); + +static void +store_msg_data_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) +{ + SmsStoreContext *ctx; + const gchar *response; + GError *error = NULL; + gint rv; + gint idx; + + response = mm_base_modem_at_command_full_finish (modem, res, &error); + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + /* Read the new part index from the reply */ + rv = sscanf (response, "+CMGW: %d", &idx); + if (rv != 1 || idx < 0) { + g_task_return_new_error (task, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't read index of already stored part: " + "%d fields parsed", + rv); + g_object_unref (task); + return; + } + + ctx = g_task_get_task_data (task); + + /* Set the index in the part we hold */ + mm_sms_part_set_index ((MMSmsPart *)ctx->current->data, (guint)idx); + + ctx->current = g_list_next (ctx->current); + sms_store_next_part (task); +} + +static void +store_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) +{ + SmsStoreContext *ctx; + GError *error = NULL; + + mm_base_modem_at_command_full_finish (modem, res, &error); + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + ctx = g_task_get_task_data (task); + + /* Send the actual message data. + * We send the data as 'raw' data because we do NOT want it to + * be treated as an AT command (i.e. we don't want it prefixed + * with AT+ and suffixed with <CR><LF>), plus, we want it to be + * sent right away (not queued after other AT commands). */ + mm_base_modem_at_command_full (ctx->modem, + ctx->port, + ctx->msg_data, + 10, + FALSE, + TRUE, /* raw */ + NULL, + (GAsyncReadyCallback)store_msg_data_ready, + task); +} + +static void +sms_store_next_part (GTask *task) +{ + MMSmsAt *self; + SmsStoreContext *ctx; + GError *error = NULL; + g_autofree gchar *cmd = NULL; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + if (!ctx->current) { + /* Done we are */ + g_task_return_boolean (task, TRUE); + g_object_unref (task); + return; + } + + g_clear_pointer (&ctx->msg_data, g_free); + + if (!sms_get_store_or_send_command (self, + (MMSmsPart *)ctx->current->data, + ctx->use_pdu_mode, + FALSE, + &cmd, + &ctx->msg_data, + &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + g_assert (cmd != NULL); + g_assert (ctx->msg_data != NULL); + + mm_base_modem_at_command_full (ctx->modem, + ctx->port, + cmd, + 10, + FALSE, + FALSE, /* raw */ + NULL, + (GAsyncReadyCallback)store_ready, + task); +} + +static void +store_lock_sms_storages_ready (MMIfaceModemMessaging *messaging, + GAsyncResult *res, + GTask *task) +{ + MMSmsAt *self; + SmsStoreContext *ctx; + GError *error = NULL; + + if (!mm_iface_modem_messaging_lock_storages_finish (messaging, res, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + /* We are now locked. Whatever result we have here, we need to make sure + * we unlock the storages before finishing. */ + ctx->need_unlock = TRUE; + + /* Go on to store the parts */ + ctx->current = mm_base_sms_get_parts (MM_BASE_SMS (self)); + sms_store_next_part (task); +} + +static void +sms_store (MMBaseSms *sms, + MMSmsStorage storage, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MMSmsAt *self = MM_SMS_AT (sms); + SmsStoreContext *ctx; + GTask *task; + MMIfacePortAt *port; + GError *error = NULL; + + task = g_task_new (self, NULL, callback, user_data); + + /* Select port for the operation */ + port = mm_base_modem_peek_best_at_port (self->priv->modem, &error); + if (!port) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + /* Setup the context */ + ctx = g_slice_new0 (SmsStoreContext); + ctx->modem = g_object_ref (self->priv->modem); + ctx->port = g_object_ref (port); + ctx->storage = storage; + + /* Different ways to do it if on PDU or text mode */ + g_assert (MM_IS_IFACE_MODEM_MESSAGING (self->priv->modem)); + g_object_get (self->priv->modem, + MM_IFACE_MODEM_MESSAGING_SMS_PDU_MODE, &ctx->use_pdu_mode, + NULL); + g_task_set_task_data (task, ctx, (GDestroyNotify)sms_store_context_free); + + /* First, lock storage to use */ + mm_iface_modem_messaging_lock_storages ( + MM_IFACE_MODEM_MESSAGING (self->priv->modem), + MM_SMS_STORAGE_UNKNOWN, /* none required for mem1 */ + ctx->storage, + (GAsyncReadyCallback)store_lock_sms_storages_ready, + task); +} + +/*****************************************************************************/ +/* Send the SMS */ + +typedef struct { + MMBaseModem *modem; + MMIfacePortAt *port; + gboolean need_unlock; + gboolean from_storage; + gboolean use_pdu_mode; + GList *current; + gchar *msg_data; +} SmsSendContext; + +static void +sms_send_context_free (SmsSendContext *ctx) +{ + /* Unlock mem2 storage if we had the lock */ + if (ctx->need_unlock) { + mm_iface_modem_messaging_unlock_storages (MM_IFACE_MODEM_MESSAGING (ctx->modem), + FALSE, + TRUE); + } + g_object_unref (ctx->port); + g_object_unref (ctx->modem); + g_free (ctx->msg_data); + g_slice_free (SmsSendContext, ctx); +} + +static gboolean +sms_send_finish (MMBaseSms *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void sms_send_next_part (GTask *task); + +static gint +read_message_reference_from_reply (const gchar *response, + GError **error) +{ + gint rv = 0; + gint idx = -1; + + if (strstr (response, "+CMGS")) + rv = sscanf (strstr (response, "+CMGS"), "+CMGS: %d", &idx); + else if (strstr (response, "+CMSS")) + rv = sscanf (strstr (response, "+CMSS"), "+CMSS: %d", &idx); + + if (rv != 1 || idx < 0) { + g_set_error (error, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't read message reference: " + "%d fields parsed from response '%s'", + rv, response); + return -1; + } + + return idx; +} + +static void +send_generic_msg_data_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) +{ + SmsSendContext *ctx; + GError *error = NULL; + const gchar *response; + gint message_reference; + + response = mm_base_modem_at_command_full_finish (modem, res, &error); + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + message_reference = read_message_reference_from_reply (response, &error); + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + ctx = g_task_get_task_data (task); + + mm_sms_part_set_message_reference ((MMSmsPart *)ctx->current->data, + (guint)message_reference); + + ctx->current = g_list_next (ctx->current); + sms_send_next_part (task); +} + +static void +send_generic_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) +{ + SmsSendContext *ctx; + GError *error = NULL; + + mm_base_modem_at_command_full_finish (modem, res, &error); + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + ctx = g_task_get_task_data (task); + + /* Send the actual message data. + * We send the data as 'raw' data because we do NOT want it to + * be treated as an AT command (i.e. we don't want it prefixed + * with AT+ and suffixed with <CR><LF>), plus, we want it to be + * sent right away (not queued after other AT commands). */ + mm_base_modem_at_command_full (ctx->modem, + ctx->port, + ctx->msg_data, + MM_BASE_SMS_DEFAULT_SEND_TIMEOUT, + FALSE, + TRUE, /* raw */ + NULL, + (GAsyncReadyCallback)send_generic_msg_data_ready, + task); +} + +static void +send_from_storage_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) +{ + MMSmsAt *self; + SmsSendContext *ctx; + GError *error = NULL; + const gchar *response; + gint message_reference; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + response = mm_base_modem_at_command_full_finish (modem, res, &error); + if (error) { + if (g_error_matches (error, MM_SERIAL_ERROR, MM_SERIAL_ERROR_RESPONSE_TIMEOUT)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + mm_obj_dbg (self, "couldn't send SMS from storage: %s; trying generic send...", error->message); + g_error_free (error); + + ctx->from_storage = FALSE; + sms_send_next_part (task); + return; + } + + message_reference = read_message_reference_from_reply (response, &error); + if (error) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + mm_sms_part_set_message_reference ((MMSmsPart *)ctx->current->data, + (guint)message_reference); + + ctx->current = g_list_next (ctx->current); + sms_send_next_part (task); +} + +static void +sms_send_next_part (GTask *task) +{ + MMSmsAt *self; + SmsSendContext *ctx; + GError *error = NULL; + g_autofree gchar *cmd = NULL; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + if (!ctx->current) { + /* Done we are */ + g_task_return_boolean (task, TRUE); + g_object_unref (task); + return; + } + + /* Send from storage */ + if (ctx->from_storage) { + cmd = g_strdup_printf ("+CMSS=%d", mm_sms_part_get_index ((MMSmsPart *)ctx->current->data)); + mm_base_modem_at_command_full (ctx->modem, + ctx->port, + cmd, + MM_BASE_SMS_DEFAULT_SEND_TIMEOUT, + FALSE, + FALSE, + NULL, + (GAsyncReadyCallback)send_from_storage_ready, + task); + return; + } + + /* Generic send */ + + g_clear_pointer (&ctx->msg_data, g_free); + + if (!sms_get_store_or_send_command (self, + (MMSmsPart *)ctx->current->data, + ctx->use_pdu_mode, + TRUE, + &cmd, + &ctx->msg_data, + &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + g_assert (cmd != NULL); + g_assert (ctx->msg_data != NULL); + + /* no network involved in this initial AT command, so lower timeout */ + mm_base_modem_at_command_full (ctx->modem, + ctx->port, + cmd, + 10, + FALSE, + FALSE, /* raw */ + NULL, + (GAsyncReadyCallback)send_generic_ready, + task); +} + +static void +send_lock_sms_storages_ready (MMIfaceModemMessaging *messaging, + GAsyncResult *res, + GTask *task) +{ + MMSmsAt *self; + SmsSendContext *ctx; + GError *error = NULL; + + if (!mm_iface_modem_messaging_lock_storages_finish (messaging, res, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + /* We are now locked. Whatever result we have here, we need to make sure + * we unlock the storages before finishing. */ + ctx->need_unlock = TRUE; + + /* Go on to send the parts */ + ctx->current = mm_base_sms_get_parts (MM_BASE_SMS (self)); + sms_send_next_part (task); +} + +static void +sms_send (MMBaseSms *sms, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MMSmsAt *self = MM_SMS_AT (sms); + SmsSendContext *ctx; + GTask *task; + MMIfacePortAt *port; + GError *error = NULL; + + task = g_task_new (self, NULL, callback, user_data); + + /* Select port for the operation */ + port = mm_base_modem_peek_best_at_port (self->priv->modem, &error); + if (!port) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + /* Setup the context */ + ctx = g_slice_new0 (SmsSendContext); + ctx->modem = g_object_ref (self->priv->modem); + ctx->port = g_object_ref (port); + g_task_set_task_data (task, ctx, (GDestroyNotify)sms_send_context_free); + + /* If the SMS is STORED, try to send from storage */ + ctx->from_storage = (mm_base_sms_get_storage (MM_BASE_SMS (self)) != MM_SMS_STORAGE_UNKNOWN); + if (ctx->from_storage) { + /* When sending from storage, first lock storage to use */ + g_assert (MM_IS_IFACE_MODEM_MESSAGING (self->priv->modem)); + mm_iface_modem_messaging_lock_storages ( + MM_IFACE_MODEM_MESSAGING (self->priv->modem), + MM_SMS_STORAGE_UNKNOWN, /* none required for mem1 */ + mm_base_sms_get_storage (MM_BASE_SMS (self)), + (GAsyncReadyCallback)send_lock_sms_storages_ready, + task); + return; + } + + /* Different ways to do it if on PDU or text mode */ + g_object_get (self->priv->modem, + MM_IFACE_MODEM_MESSAGING_SMS_PDU_MODE, &ctx->use_pdu_mode, + NULL); + ctx->current = mm_base_sms_get_parts (MM_BASE_SMS (self)); + sms_send_next_part (task); +} + +/*****************************************************************************/ + +typedef struct { + MMBaseModem *modem; + gboolean need_unlock; + GList *current; + guint n_failed; +} SmsDeletePartsContext; + +static void +sms_delete_parts_context_free (SmsDeletePartsContext *ctx) +{ + /* Unlock mem1 storage if we had the lock */ + if (ctx->need_unlock) { + mm_iface_modem_messaging_unlock_storages (MM_IFACE_MODEM_MESSAGING (ctx->modem), + TRUE, + FALSE); + } + g_object_unref (ctx->modem); + g_slice_free (SmsDeletePartsContext, ctx); +} + +static gboolean +sms_delete_finish (MMBaseSms *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void delete_next_part (GTask *task); + +static void +delete_part_ready (MMBaseModem *modem, + GAsyncResult *res, + GTask *task) +{ + MMSmsAt *self; + SmsDeletePartsContext *ctx; + g_autoptr(GError) error = NULL; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + mm_base_modem_at_command_finish (modem, res, &error); + if (error) { + ctx->n_failed++; + mm_obj_dbg (self, "couldn't delete SMS part with index %u: %s", + mm_sms_part_get_index ((MMSmsPart *)ctx->current->data), + error->message); + } + + /* We reset the index, as there is no longer that part */ + mm_sms_part_set_index ((MMSmsPart *)ctx->current->data, SMS_PART_INVALID_INDEX); + + ctx->current = g_list_next (ctx->current); + delete_next_part (task); +} + +static void +delete_next_part (GTask *task) +{ + SmsDeletePartsContext *ctx; + g_autofree gchar *cmd = NULL; + + ctx = g_task_get_task_data (task); + + /* Skip non-stored parts */ + while (ctx->current && (mm_sms_part_get_index ((MMSmsPart *)ctx->current->data) == SMS_PART_INVALID_INDEX)) + ctx->current = g_list_next (ctx->current); + + /* If all removed, we're done */ + if (!ctx->current) { + if (ctx->n_failed > 0) + g_task_return_new_error (task, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't delete %u parts from this SMS", + ctx->n_failed); + else + g_task_return_boolean (task, TRUE); + g_object_unref (task); + return; + } + + cmd = g_strdup_printf ("+CMGD=%d", mm_sms_part_get_index ((MMSmsPart *)ctx->current->data)); + mm_base_modem_at_command (ctx->modem, + cmd, + 10, + FALSE, + (GAsyncReadyCallback)delete_part_ready, + task); +} + +static void +delete_lock_sms_storages_ready (MMIfaceModemMessaging *messaging, + GAsyncResult *res, + GTask *task) +{ + MMSmsAt *self; + SmsDeletePartsContext *ctx; + GError *error = NULL; + + if (!mm_iface_modem_messaging_lock_storages_finish (messaging, res, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + + /* We are now locked. Whatever result we have here, we need to make sure + * we unlock the storages before finishing. */ + ctx->need_unlock = TRUE; + + /* Go on deleting parts */ + ctx->current = mm_base_sms_get_parts (MM_BASE_SMS (self)); + delete_next_part (task); +} + +static void +sms_delete (MMBaseSms *sms, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MMSmsAt *self = MM_SMS_AT (sms); + SmsDeletePartsContext *ctx; + GTask *task; + + ctx = g_slice_new0 (SmsDeletePartsContext); + ctx->modem = g_object_ref (self->priv->modem); + + task = g_task_new (self, NULL, callback, user_data); + g_task_set_task_data (task, ctx, (GDestroyNotify)sms_delete_parts_context_free); + + if (mm_base_sms_get_storage (MM_BASE_SMS (self)) == MM_SMS_STORAGE_UNKNOWN) { + mm_obj_dbg (self, "not removing parts from non-stored SMS"); + g_task_return_boolean (task, TRUE); + g_object_unref (task); + return; + } + + /* Select specific storage to delete from */ + mm_iface_modem_messaging_lock_storages ( + MM_IFACE_MODEM_MESSAGING (self->priv->modem), + mm_base_sms_get_storage (MM_BASE_SMS (self)), + MM_SMS_STORAGE_UNKNOWN, /* none required for mem2 */ + (GAsyncReadyCallback)delete_lock_sms_storages_ready, + task); +} + +/*****************************************************************************/ + +MMBaseSms * +mm_sms_at_new (MMBaseModem *modem, gboolean is_3gpp) +{ + MMBaseSms *sms; + + sms = MM_BASE_SMS (g_object_new (MM_TYPE_SMS_AT, + MM_BASE_SMS_MODEM, modem, + MM_BASE_SMS_IS_3GPP, is_3gpp, + NULL)); + MM_SMS_AT (sms)->priv->modem = g_object_ref (modem); + return sms; +} + +static void +mm_sms_at_init (MMSmsAt *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MM_TYPE_SMS_AT, MMSmsAtPrivate); +} + +static void +dispose (GObject *object) +{ + MMSmsAt *self = MM_SMS_AT (object); + + g_clear_object (&self->priv->modem); + + G_OBJECT_CLASS (mm_sms_at_parent_class)->dispose (object); +} + +static void +mm_sms_at_class_init (MMSmsAtClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + MMBaseSmsClass *base_sms_class = MM_BASE_SMS_CLASS (klass); + + g_type_class_add_private (object_class, sizeof (MMSmsAtPrivate)); + + object_class->dispose = dispose; + + base_sms_class->store = sms_store; + base_sms_class->store_finish = sms_store_finish; + base_sms_class->send = sms_send; + base_sms_class->send_finish = sms_send_finish; + base_sms_class->delete = sms_delete; + base_sms_class->delete_finish = sms_delete_finish; +} diff --git a/src/mm-sms-at.h b/src/mm-sms-at.h new file mode 100644 index 00000000..26e2dae4 --- /dev/null +++ b/src/mm-sms-at.h @@ -0,0 +1,59 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * Author: Aleksander Morgado <aleksander@lanedo.com> + * + * Copyright (C) 2012 Google, Inc. + * Copyright (C) 2025 Dan Williams <dan@ioncontrol.co> + */ + +#ifndef MM_SMS_AT_H +#define MM_SMS_AT_H + +#include <glib.h> +#include <glib-object.h> + +#define _LIBMM_INSIDE_MM +#include <libmm-glib.h> + +#include "mm-base-sms.h" +#include "mm-base-modem.h" + +/*****************************************************************************/ + +#define MM_TYPE_SMS_AT (mm_sms_at_get_type ()) +#define MM_SMS_AT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MM_TYPE_SMS_AT, MMSmsAt)) +#define MM_SMS_AT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MM_TYPE_SMS_AT, MMSmsAtClass)) +#define MM_IS_SMS_AT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MM_TYPE_SMS_AT)) +#define MM_IS_SMS_AT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MM_TYPE_SMS_AT)) +#define MM_SMS_AT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MM_TYPE_SMS_AT, MMSmsAtClass)) + +typedef struct _MMSmsAt MMSmsAt; +typedef struct _MMSmsAtClass MMSmsAtClass; +typedef struct _MMSmsAtPrivate MMSmsAtPrivate; + +struct _MMSmsAt { + MMBaseSms parent; + MMSmsAtPrivate *priv; +}; + +struct _MMSmsAtClass { + MMBaseSmsClass parent; +}; + +GType mm_sms_at_get_type (void); +G_DEFINE_AUTOPTR_CLEANUP_FUNC (MMSmsAt, g_object_unref) + +MMBaseSms *mm_sms_at_new (MMBaseModem *modem, + gboolean is_3gpp); + +#endif /* MM_SMS_AT_H */ |