diff options
-rw-r--r-- | src/mm-broadband-modem-qmi.c | 456 | ||||
-rw-r--r-- | src/mm-modem-helpers-qmi.c | 16 | ||||
-rw-r--r-- | src/mm-modem-helpers-qmi.h | 1 |
3 files changed, 470 insertions, 3 deletions
diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c index 66ad00ad..371e910b 100644 --- a/src/mm-broadband-modem-qmi.c +++ b/src/mm-broadband-modem-qmi.c @@ -36,6 +36,7 @@ #include "mm-iface-modem-3gpp.h" #include "mm-iface-modem-3gpp-profile-manager.h" #include "mm-iface-modem-3gpp-ussd.h" +#include "mm-iface-modem-cell-broadcast.h" #include "mm-iface-modem-voice.h" #include "mm-iface-modem-cdma.h" #include "mm-iface-modem-messaging.h" @@ -58,6 +59,7 @@ static void iface_modem_3gpp_init (MMIfaceModem3gppInterface static void iface_modem_3gpp_profile_manager_init (MMIfaceModem3gppProfileManagerInterface *iface); static void iface_modem_3gpp_ussd_init (MMIfaceModem3gppUssdInterface *iface); static void iface_modem_voice_init (MMIfaceModemVoiceInterface *iface); +static void iface_modem_cell_broadcast_init (MMIfaceModemCellBroadcastInterface *iface); static void iface_modem_cdma_init (MMIfaceModemCdmaInterface *iface); static void iface_modem_messaging_init (MMIfaceModemMessagingInterface *iface); static void iface_modem_location_init (MMIfaceModemLocationInterface *iface); @@ -67,15 +69,17 @@ static void iface_modem_sar_init (MMIfaceModemSarInterface static void iface_modem_signal_init (MMIfaceModemSignalInterface *iface); static void shared_qmi_init (MMSharedQmi *iface); -static MMIfaceModemLocationInterface *iface_modem_location_parent; -static MMIfaceModemMessagingInterface *iface_modem_messaging_parent; -static MMIfaceModemVoiceInterface *iface_modem_voice_parent; +static MMIfaceModemCellBroadcastInterface *iface_modem_cell_broadcast_parent; +static MMIfaceModemLocationInterface *iface_modem_location_parent; +static MMIfaceModemMessagingInterface *iface_modem_messaging_parent; +static MMIfaceModemVoiceInterface *iface_modem_voice_parent; G_DEFINE_TYPE_EXTENDED (MMBroadbandModemQmi, mm_broadband_modem_qmi, MM_TYPE_BROADBAND_MODEM, 0, G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM, iface_modem_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP, iface_modem_3gpp_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_PROFILE_MANAGER, iface_modem_3gpp_profile_manager_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_3GPP_USSD, iface_modem_3gpp_ussd_init) + G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_CELL_BROADCAST, iface_modem_cell_broadcast_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_VOICE, iface_modem_voice_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_CDMA, iface_modem_cdma_init) G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_MESSAGING, iface_modem_messaging_init) @@ -164,6 +168,11 @@ struct _MMBroadbandModemQmiPrivate { GList *firmware_list; MMFirmwareProperties *current_firmware; + /* Cell Broadcast helpers */ + gboolean cell_broadcast_fallback_at_only; + guint cell_broadcast_event_report_indication_id; + gboolean cell_broadcast_unsolicited_events_setup; + /* For notifying when the qmi-proxy connection is dead */ guint qmi_device_removed_id; @@ -10533,6 +10542,433 @@ modem_3gpp_ussd_cancel (MMIfaceModem3gppUssd *_self, } /*****************************************************************************/ +/* Check support (CellBroadcast interface) */ + +static gboolean +cell_broadcast_check_support_finish (MMIfaceModemCellBroadcast *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +parent_cell_broadcast_check_support_ready (MMIfaceModemCellBroadcast *_self, + GAsyncResult *res, + GTask *task) +{ + MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); + + self->priv->cell_broadcast_fallback_at_only = iface_modem_cell_broadcast_parent->check_support_finish (_self, res, NULL); + + g_task_return_boolean (task, self->priv->cell_broadcast_fallback_at_only); + g_object_unref (task); +} + +static void +cell_broadcast_check_support (MMIfaceModemCellBroadcast *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + task = g_task_new (self, NULL, callback, user_data); + + /* If we have support for the WMS client, cell_broadcast is supported */ + if (!mm_shared_qmi_peek_client (MM_SHARED_QMI (self), + QMI_SERVICE_WMS, + MM_PORT_QMI_FLAG_DEFAULT, + NULL)) { + /* Try to fallback to AT support */ + iface_modem_cell_broadcast_parent->check_support ( + self, + (GAsyncReadyCallback)parent_cell_broadcast_check_support_ready, + task); + return; + } + + mm_obj_dbg (self, "Cell Broadcast capabilities supported"); + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +/*****************************************************************************/ +/* Common setup/cleanup unsolicited event handlers (CellBroadcast interface) */ + +typedef struct { + MMBroadbandModemQmi *self; + QmiClientWms *client; + QmiWmsStorageType storage; + guint32 memory_index; + QmiWmsMessageMode message_mode; +} CbsIndicationRawReadContext; + +static void +cbs_indication_raw_read_context_free (CbsIndicationRawReadContext *ctx) +{ + g_object_unref (ctx->client); + g_object_unref (ctx->self); + g_slice_free (CbsIndicationRawReadContext, ctx); +} + +static void +add_new_read_cbm_part (MMIfaceModemCellBroadcast *self, + QmiWmsMessageTagType tag, + QmiWmsMessageFormat format, + gboolean transfer_route, + GArray *data) +{ + MMCbmPart *part = NULL; + GError *error = NULL; + + switch (format) { + /* Cell Broadcasts need to be broadcast messages */ + case QMI_WMS_MESSAGE_FORMAT_GSM_WCDMA_BROADCAST: + part = mm_cbm_part_new_from_binary_pdu ((guint8 *)data->data, + data->len, + self, + &error); + break; + case QMI_WMS_MESSAGE_FORMAT_MWI: + case QMI_WMS_MESSAGE_FORMAT_GSM_WCDMA_POINT_TO_POINT: + case QMI_WMS_MESSAGE_FORMAT_CDMA: + default: + mm_obj_dbg (self, "unhandled message format '%u'", format); + break; + } + + if (part) { + mm_obj_dbg (self, "correctly parsed PDU"); + mm_iface_modem_cell_broadcast_take_part (self, + part, + mm_cbm_state_from_qmi_message_tag (tag)); + } else if (error) { + /* Don't treat the error as critical */ + mm_obj_dbg (self, "error parsing PDU: %s", error->message); + g_error_free (error); + } +} + +static void +cbs_wms_indication_raw_read_ready (QmiClientWms *client, + GAsyncResult *res, + CbsIndicationRawReadContext *ctx) +{ + QmiMessageWmsRawReadOutput *output = NULL; + GError *error = NULL; + + /* Ignore errors */ + + output = qmi_client_wms_raw_read_finish (client, res, &error); + if (!output) { + mm_obj_dbg (ctx->self, "QMI operation failed: %s", error->message); + g_error_free (error); + } else if (!qmi_message_wms_raw_read_output_get_result (output, &error)) { + mm_obj_dbg (ctx->self, "couldn't read raw message: %s", error->message); + g_error_free (error); + } else { + QmiWmsMessageTagType tag; + QmiWmsMessageFormat format; + GArray *data; + + qmi_message_wms_raw_read_output_get_raw_message_data ( + output, + &tag, + &format, + &data, + NULL); + add_new_read_cbm_part (MM_IFACE_MODEM_CELL_BROADCAST (ctx->self), + tag, + format, + FALSE, + data); + } + + if (output) + qmi_message_wms_raw_read_output_unref (output); + + cbs_indication_raw_read_context_free (ctx); +} + +static void +cbs_wms_send_ack_ready (QmiClientWms *client, + GAsyncResult *res, + MMBroadbandModemQmi *self) +{ + g_autoptr(QmiMessageWmsSendAckOutput) output = NULL; + g_autoptr(GError) error= NULL; + + output = qmi_client_wms_send_ack_finish (client, res, &error); + if (!output) { + mm_obj_dbg (self, "QMI operation failed: '%s'", error->message); + } + g_object_unref (self); +} + +static void +cell_broadcast_event_report_indication_cb (QmiClientNas *client, + QmiIndicationWmsEventReportOutput *output, + MMBroadbandModemQmi *self) +{ + QmiWmsStorageType storage; + guint32 memory_index; + QmiWmsAckIndicator ack_ind; + guint32 transaction_id; + QmiWmsMessageFormat msg_format; + QmiWmsMessageTagType tag; + GArray *raw_data = NULL; + + /* Handle transfer-route MT messages */ + if (qmi_indication_wms_event_report_output_get_transfer_route_mt_message (output, + &ack_ind, + &transaction_id, + &msg_format, + &raw_data, + NULL)) { + mm_obj_dbg (self, "Got transfer-route MT message"); + /* If this is the first of a multi-part message, send an ACK to get the + * second part */ + if (ack_ind == QMI_WMS_ACK_INDICATOR_SEND) { + g_autoptr(QmiMessageWmsSendAckInput) ack_input = NULL; + QmiWmsMessageProtocol message_protocol; + /* Need to ack message */ + mm_obj_dbg (self, "Need to ACK indicator"); + switch (msg_format) { + case QMI_WMS_MESSAGE_FORMAT_CDMA: + message_protocol = QMI_WMS_MESSAGE_PROTOCOL_CDMA; + break; + case QMI_WMS_MESSAGE_FORMAT_MWI: + case QMI_WMS_MESSAGE_FORMAT_GSM_WCDMA_POINT_TO_POINT: + case QMI_WMS_MESSAGE_FORMAT_GSM_WCDMA_BROADCAST: + default: + message_protocol = QMI_WMS_MESSAGE_PROTOCOL_WCDMA; + break; + } + ack_input = qmi_message_wms_send_ack_input_new(); + qmi_message_wms_send_ack_input_set_information (ack_input, + transaction_id, + message_protocol, + TRUE, + NULL); + qmi_client_wms_send_ack (QMI_CLIENT_WMS (client), + ack_input, + MM_BASE_SMS_DEFAULT_SEND_TIMEOUT, + NULL, + (GAsyncReadyCallback)cbs_wms_send_ack_ready, + g_object_ref (self)); + } + + /* Defaults for transfer-route messages, which are not stored anywhere */ + storage = QMI_WMS_STORAGE_TYPE_NONE; + tag = QMI_WMS_MESSAGE_TAG_TYPE_MT_NOT_READ; + add_new_read_cbm_part (MM_IFACE_MODEM_CELL_BROADCAST (self), + tag, + msg_format, + TRUE, + raw_data); + return; + } + + if (qmi_indication_wms_event_report_output_get_mt_message ( + output, + &storage, + &memory_index, + NULL)) { + CbsIndicationRawReadContext *ctx; + QmiMessageWmsRawReadInput *input; + + ctx = g_slice_new (CbsIndicationRawReadContext); + ctx->self = g_object_ref (self); + ctx->client = QMI_CLIENT_WMS (g_object_ref (client)); + ctx->storage = storage; + ctx->memory_index = memory_index; + + input = qmi_message_wms_raw_read_input_new (); + qmi_message_wms_raw_read_input_set_message_memory_storage_id ( + input, + storage, + memory_index, + NULL); + + /* Default to 3GPP message mode if none given */ + if (!qmi_indication_wms_event_report_output_get_message_mode ( + output, + &ctx->message_mode, + NULL)) + ctx->message_mode = QMI_WMS_MESSAGE_MODE_GSM_WCDMA; + qmi_message_wms_raw_read_input_set_message_mode ( + input, + ctx->message_mode, + NULL); + + qmi_client_wms_raw_read (QMI_CLIENT_WMS (client), + input, + 3, + NULL, + (GAsyncReadyCallback)cbs_wms_indication_raw_read_ready, + ctx); + qmi_message_wms_raw_read_input_unref (input); + } +} + +static gboolean +common_setup_cleanup_cell_broadcast_unsolicited_events (MMBroadbandModemQmi *self, + gboolean enable, + GError **error) +{ + QmiClient *client = NULL; + + client = mm_shared_qmi_peek_client (MM_SHARED_QMI (self), + QMI_SERVICE_WMS, + MM_PORT_QMI_FLAG_DEFAULT, + error); + if (!client) + return FALSE; + + if (enable == self->priv->cell_broadcast_unsolicited_events_setup) { + mm_obj_dbg (self, "cell broadcast unsolicited events already %s; skipping", + enable ? "setup" : "cleanup"); + return TRUE; + } + + /* Store new state */ + self->priv->cell_broadcast_unsolicited_events_setup = enable; + + /* Connect/Disconnect "Event Report" indications */ + if (enable) { + g_assert (self->priv->cell_broadcast_event_report_indication_id == 0); + self->priv->cell_broadcast_event_report_indication_id = + g_signal_connect (client, + "event-report", + G_CALLBACK (cell_broadcast_event_report_indication_cb), + self); + } else { + g_assert (self->priv->cell_broadcast_event_report_indication_id != 0); + g_signal_handler_disconnect (client, self->priv->cell_broadcast_event_report_indication_id); + self->priv->cell_broadcast_event_report_indication_id = 0; + } + + return TRUE; +} + +/*****************************************************************************/ +/* Cleanup unsolicited event handlers (CellBroadcast interface) */ + +static gboolean +cell_broadcast_cleanup_unsolicited_events_finish (MMIfaceModemCellBroadcast *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +parent_cell_broadcast_cleanup_unsolicited_events_ready (MMIfaceModemCellBroadcast *_self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + + if (!iface_modem_cell_broadcast_parent->cleanup_unsolicited_events_finish (_self, res, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +static void +cell_broadcast_cleanup_unsolicited_events (MMIfaceModemCellBroadcast *_self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); + GTask *task = g_task_new (self, NULL, callback, user_data); + GError *error = NULL; + + if (self->priv->cell_broadcast_fallback_at_only) { + iface_modem_cell_broadcast_parent->cleanup_unsolicited_events ( + _self, + (GAsyncReadyCallback)parent_cell_broadcast_cleanup_unsolicited_events_ready, + task); + } else { + if (!common_setup_cleanup_cell_broadcast_unsolicited_events (self, FALSE, &error)) + g_task_return_error (task, error); + else + g_task_return_boolean (task, TRUE); + } +} + +/*****************************************************************************/ +/* Setup unsolicited event handlers (CellBroadcast interface) */ + +static gboolean +cell_broadcast_setup_unsolicited_events_finish (MMIfaceModemCellBroadcast *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (res), error); +} + +static void +parent_cell_broadcast_setup_unsolicited_events_ready (MMIfaceModemCellBroadcast *_self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + + if (!iface_modem_cell_broadcast_parent->setup_unsolicited_events_finish (_self, res, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +static void +cell_broadcast_setup_unsolicited_events (MMIfaceModemCellBroadcast *_self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); + GTask *task = g_task_new (self, NULL, callback, user_data); + GError *error = NULL; + + /* Handle AT URC only fallback */ + if (self->priv->cell_broadcast_fallback_at_only) { + iface_modem_cell_broadcast_parent->setup_unsolicited_events (_self, + (GAsyncReadyCallback)parent_cell_broadcast_setup_unsolicited_events_ready, + task); + } else { + /* Enable QMI indications */ + if (!common_setup_cleanup_cell_broadcast_unsolicited_events (self, TRUE, &error)) + g_task_return_error (task, error); + else + g_task_return_boolean (task, TRUE); + } +} + +/*****************************************************************************/ +/* Create CBM (CellBroadcast interface) */ + +static MMBaseCbm * +cell_broadcast_create_cbm (MMIfaceModemCellBroadcast *_self) +{ + MMBroadbandModemQmi *self = MM_BROADBAND_MODEM_QMI (_self); + + /* Handle AT URC only fallback */ + if (self->priv->cell_broadcast_fallback_at_only) { + return iface_modem_cell_broadcast_parent->create_cbm (_self); + } + + return mm_base_cbm_new (MM_BASE_MODEM (self)); +} + +/*****************************************************************************/ /* Check support (Voice interface) */ static gboolean @@ -14290,6 +14726,20 @@ iface_modem_cdma_init (MMIfaceModemCdmaInterface *iface) } static void +iface_modem_cell_broadcast_init (MMIfaceModemCellBroadcastInterface *iface) +{ + iface_modem_cell_broadcast_parent = g_type_interface_peek_parent (iface); + + iface->check_support = cell_broadcast_check_support; + iface->check_support_finish = cell_broadcast_check_support_finish; + iface->setup_unsolicited_events = cell_broadcast_setup_unsolicited_events; + iface->setup_unsolicited_events_finish = cell_broadcast_setup_unsolicited_events_finish; + iface->cleanup_unsolicited_events = cell_broadcast_cleanup_unsolicited_events; + iface->cleanup_unsolicited_events_finish = cell_broadcast_cleanup_unsolicited_events_finish; + iface->create_cbm = cell_broadcast_create_cbm; +} + +static void iface_modem_messaging_init (MMIfaceModemMessagingInterface *iface) { iface_modem_messaging_parent = g_type_interface_peek_parent (iface); diff --git a/src/mm-modem-helpers-qmi.c b/src/mm-modem-helpers-qmi.c index 7a87454b..ea09b9d8 100644 --- a/src/mm-modem-helpers-qmi.c +++ b/src/mm-modem-helpers-qmi.c @@ -2251,6 +2251,22 @@ mm_sms_state_from_qmi_message_tag (QmiWmsMessageTagType tag) /*****************************************************************************/ +MMCbmState +mm_cbm_state_from_qmi_message_tag (QmiWmsMessageTagType tag) +{ + switch (tag) { + case QMI_WMS_MESSAGE_TAG_TYPE_MT_READ: + case QMI_WMS_MESSAGE_TAG_TYPE_MT_NOT_READ: + return MM_CBM_STATE_RECEIVED; + case QMI_WMS_MESSAGE_TAG_TYPE_MO_SENT: + case QMI_WMS_MESSAGE_TAG_TYPE_MO_NOT_SENT: + default: + return MM_CBM_STATE_UNKNOWN; + } +} + +/*****************************************************************************/ + QmiWdsAuthentication mm_bearer_allowed_auth_to_qmi_authentication (MMBearerAllowedAuth auth, gpointer log_object, diff --git a/src/mm-modem-helpers-qmi.h b/src/mm-modem-helpers-qmi.h index 75fdd5f7..4f97bb65 100644 --- a/src/mm-modem-helpers-qmi.h +++ b/src/mm-modem-helpers-qmi.h @@ -136,6 +136,7 @@ QmiWmsStorageType mm_sms_storage_to_qmi_storage_type (MMSmsStorage storage); MMSmsStorage mm_sms_storage_from_qmi_storage_type (QmiWmsStorageType qmi_storage); MMSmsState mm_sms_state_from_qmi_message_tag (QmiWmsMessageTagType tag); +MMCbmState mm_cbm_state_from_qmi_message_tag (QmiWmsMessageTagType tag); /*****************************************************************************/ /* QMI/WDS to MM translations */ |