diff options
author | Akash Aggarwal <quic_akasagga@quicinc.com> | 2022-02-04 15:33:22 +0530 |
---|---|---|
committer | Akash Aggarwal <quic_akasagga@quicinc.com> | 2022-09-09 18:51:26 +0530 |
commit | af9b6d4f2d7e43e2092d16afdae7ad38f7f55163 (patch) | |
tree | 56b3a161fcb646d05443ee8029406eecae1262e0 /src/mm-bearer-qmi.c | |
parent | 61e540c8d9bfde58333eb46e81fc825c5651764c (diff) |
mm-bearer-qmi: Add Support for PCO
Get PCO information after the call is established
by using get runtime settings request
Register for Extended IP configuration Indication
If PCO is changed we get the indication and refetch
the pco information from modem
Send the PCO info using 3gpp_update_pco_list
Diffstat (limited to 'src/mm-bearer-qmi.c')
-rw-r--r-- | src/mm-bearer-qmi.c | 280 |
1 files changed, 278 insertions, 2 deletions
diff --git a/src/mm-bearer-qmi.c b/src/mm-bearer-qmi.c index 57a30aa5..ef7ac1b1 100644 --- a/src/mm-bearer-qmi.c +++ b/src/mm-bearer-qmi.c @@ -12,6 +12,7 @@ * * Copyright (C) 2012 Google, Inc. * Copyright (C) 2015 Azimut Electronics + * Copyright (c) 2022 Qualcomm Innovation Center, Inc. */ #include <config.h> @@ -27,6 +28,7 @@ #define _LIBMM_INSIDE_MM #include <libmm-glib.h> +#include "mm-iface-modem-3gpp.h" #include "mm-iface-modem.h" #include "mm-iface-modem-3gpp-profile-manager.h" #include "mm-bearer-qmi.h" @@ -52,16 +54,20 @@ struct _MMBearerQmiPrivate { QmiClientWds *client_ipv4; guint packet_service_status_ipv4_indication_id; guint event_report_ipv4_indication_id; + guint extended_ipv4_config_change_id; QmiClientWds *client_ipv6; guint packet_service_status_ipv6_indication_id; guint event_report_ipv6_indication_id; + guint extended_ipv6_config_change_id; MMPort *data; MMPort *link; guint mux_id; guint32 packet_data_handle_ipv4; guint32 packet_data_handle_ipv6; + + GList *pco_list; }; /*****************************************************************************/ @@ -475,6 +481,7 @@ typedef enum { CONNECT_STEP_IP_FAMILY_IPV4, CONNECT_STEP_ENABLE_INDICATIONS_IPV4, CONNECT_STEP_START_NETWORK_IPV4, + CONNECT_STEP_ENABLE_WDS_INDICATIONS_IPV4, CONNECT_STEP_GET_CURRENT_SETTINGS_IPV4, CONNECT_STEP_IPV6, CONNECT_STEP_WDS_CLIENT_IPV6, @@ -482,6 +489,7 @@ typedef enum { CONNECT_STEP_IP_FAMILY_IPV6, CONNECT_STEP_ENABLE_INDICATIONS_IPV6, CONNECT_STEP_START_NETWORK_IPV6, + CONNECT_STEP_ENABLE_WDS_INDICATIONS_IPV6, CONNECT_STEP_GET_CURRENT_SETTINGS_IPV6, CONNECT_STEP_LAST } ConnectStep; @@ -529,6 +537,8 @@ typedef struct { guint32 packet_data_handle_ipv6; MMBearerIpConfig *ipv6_config; GError *error_ipv6; + guint extended_ipv4_config_change_id; + guint extended_ipv6_config_change_id; } ConnectContext; /* When using the WDS service, we may not only want to have explicit different @@ -539,6 +549,149 @@ typedef struct { ((ctx->endpoint.type & 0xFF) << 16) | \ ((ctx->mux_id & 0xFF) << 8) | (flag & 0xFF)) +/*****************************************************************************/ +static void +process_operator_reserved_pco (MMBearerQmi *self, + QmiMessageWdsGetCurrentSettingsOutput *output) +{ + MMBaseModem *modem = NULL; + MMPco *pco; + g_autofree gchar *app_specific_info_str = NULL; + g_autoptr(GArray) array = NULL; + g_autoptr(GByteArray) pco_raw = NULL; + guint16 container_id; + guint16 tmp_mcc; + guint16 tmp_mnc; + gboolean mnc_includes_pcs_digit; + guint8 pco_prefix[9]; + gsize pco_raw_len; + + if (!qmi_message_wds_get_current_settings_output_get_operator_reserved_pco (output, &tmp_mcc, &tmp_mnc, &mnc_includes_pcs_digit, &array, &container_id, NULL)) + return ; + + app_specific_info_str = mm_utils_bin2hexstr ((guint8*) (array->data), array->len); + + mm_obj_dbg (self, "container ID: %d", container_id); + mm_obj_dbg (self, "app specific info: %s", app_specific_info_str); + + pco_raw_len = sizeof (pco_prefix) + array->len; + pco_prefix[0] = 0x27; + pco_prefix[1] = pco_raw_len - 2; + pco_prefix[2] = 0x80; + pco_prefix[3] = (container_id >> 8) & 0xFF; + pco_prefix[4] = container_id & 0xFF; + pco_prefix[5] = 3 * sizeof (guint8) + array->len; + + /* if MNC consist of 3 digits + * pco_prefix[7] = 0x<MNC digit 3><MCC digit 3> + * if MNC consist of 2 digits + * pco_prefix[7] = 0xF<MCC digit 3> + * pco_prefix[6] = 0x<MCC digit 2><MCC digit 1> + * pco_prefix[8] = 0x<MNC digit 2><MNC digit 1> + * + * e.g. from MCCMNC 311480 (MCC=311, MNC=480 with PCS digit), logic would do + * pco_prefix[7] = 0x01 | (0x00 << 4) = 0x01 + * pco_prefix[6] = 0x03 | (0x01 << 4) = 0x13 + * pco_prefix[6] = 0x04 | (0x08 << 4) = 0x84 + * And so the `pco_prefix` includes bytes `13:01:84` when the operator is 311480 (Verizon) + * + * See 3GPP TS 24.008, subclause 10.5.6.3.1 (Protocol Configuration Options) and + * 10.5.1.3 for more details on the coding of MCC and MNC. + */ + if (mnc_includes_pcs_digit) { + pco_prefix[7] = (guint8)(tmp_mcc%10) | ((guint8)(tmp_mnc%10) << 4); + tmp_mnc /= 10; + } + else + pco_prefix[7] = (guint8)(tmp_mcc%10) | 0xF0; + tmp_mcc /= 10; + pco_prefix[6] = (guint8)(tmp_mcc/10) | ((guint8)(tmp_mcc%10) << 4); + pco_prefix[8] = (guint8)(tmp_mnc/10) | ((guint8)(tmp_mnc%10) << 4); + + pco_raw = g_byte_array_sized_new (pco_raw_len); + g_byte_array_append (pco_raw, pco_prefix, sizeof (pco_prefix)); + g_byte_array_append (pco_raw, (const guint8 *)array->data, array->len); + + pco = mm_pco_new (); + /* set session ID to 0 (default) */ + mm_pco_set_session_id (pco, 0); + mm_pco_set_complete (pco, TRUE); + mm_pco_set_data (pco, pco_raw->data, pco_raw->len); + + /* mm_pco_list_add API takes care of duplicate entry */ + self->priv->pco_list = mm_pco_list_add (self->priv->pco_list, pco); + g_object_get (self, + MM_BASE_BEARER_MODEM, &modem, + NULL); + mm_iface_modem_3gpp_update_pco_list (MM_IFACE_MODEM_3GPP (modem), self->priv->pco_list); + mm_obj_dbg (self, "pco info sent successfully"); + + g_object_unref (modem); +} + +static void +get_pco_settings_ready (QmiClientWds *client, + GAsyncResult *res, + MMBearerQmi *self) +{ + g_autoptr(QmiMessageWdsGetCurrentSettingsOutput) output = NULL; + GError *error = NULL; + + output = qmi_client_wds_get_current_settings_finish (client, res, &error); + if (!output) { + mm_obj_warn (self, "error: operation failed: %s", error->message); + g_error_free (error); + g_object_unref (self); + return; + } + if (!qmi_message_wds_get_current_settings_output_get_result (output, &error)) { + mm_obj_warn (self, "error: couldn't get current settings: %s", error->message); + g_error_free (error); + g_object_unref (self); + return; + } + + process_operator_reserved_pco (self, output); + g_object_unref (self); +} + +static void +fetch_pco_data_from_modem (QmiClientWds *client, + MMBearerQmi *self) +{ + QmiMessageWdsGetCurrentSettingsInput *input; + + input = qmi_message_wds_get_current_settings_input_new (); + qmi_message_wds_get_current_settings_input_set_requested_settings ( + input, QMI_WDS_REQUESTED_SETTINGS_OPERATOR_RESERVED_PCO, NULL); + mm_obj_dbg (self, "Getting PCO Information from Modem"); + qmi_client_wds_get_current_settings (client, + input, + 10, + NULL, + (GAsyncReadyCallback) get_pco_settings_ready, + g_object_ref (self)); + qmi_message_wds_get_current_settings_input_unref (input); +} + +static void +extended_ip_config_indication_received (QmiClientWds *client, + QmiIndicationWdsExtendedIpConfigOutput *output, + MMBearerQmi *self) +{ + QmiWdsRequestedSettings mask; + g_autofree gchar *mask_str = NULL; + + if (!qmi_indication_wds_extended_ip_config_output_get_changed_ip_configuration (output, &mask, NULL)) + return; + + mask_str = qmi_wds_requested_settings_build_string_from_mask (mask); + mm_obj_dbg (self, "received extended ip type mask %s", mask_str); + if (mask & QMI_WDS_REQUESTED_SETTINGS_OPERATOR_RESERVED_PCO) + fetch_pco_data_from_modem (client, self); +} +/*****************************************************************************/ + static void connect_context_free (ConnectContext *ctx) { @@ -558,6 +711,10 @@ connect_context_free (ConnectContext *ctx) ctx->client_ipv4, &ctx->event_report_ipv4_indication_id); } + if (ctx->extended_ipv4_config_change_id) { + g_signal_handler_disconnect (ctx->client_ipv4, ctx->extended_ipv4_config_change_id); + ctx->extended_ipv4_config_change_id = 0; + } if (ctx->packet_data_handle_ipv4) { g_autoptr(QmiMessageWdsStopNetworkInput) input = NULL; @@ -580,6 +737,10 @@ connect_context_free (ConnectContext *ctx) ctx->client_ipv6, &ctx->event_report_ipv6_indication_id); } + if (ctx->extended_ipv6_config_change_id) { + g_signal_handler_disconnect (ctx->client_ipv6, ctx->extended_ipv6_config_change_id); + ctx->extended_ipv6_config_change_id = 0; + } if (ctx->packet_data_handle_ipv6) { g_autoptr(QmiMessageWdsStopNetworkInput) input = NULL; @@ -913,6 +1074,8 @@ get_current_settings_ready (QmiClientWds *client, mm_obj_dbg (self, " domains: failed (%s)", error ? error->message : "unknown"); g_clear_error (&error); } + + process_operator_reserved_pco (self, output); } if (output) @@ -939,7 +1102,8 @@ get_current_settings (GTask *task, QmiClientWds *client) QMI_WDS_REQUESTED_SETTINGS_GATEWAY_INFO | QMI_WDS_REQUESTED_SETTINGS_MTU | QMI_WDS_REQUESTED_SETTINGS_DOMAIN_NAME_LIST | - QMI_WDS_REQUESTED_SETTINGS_IP_FAMILY; + QMI_WDS_REQUESTED_SETTINGS_IP_FAMILY | + QMI_WDS_REQUESTED_SETTINGS_OPERATOR_RESERVED_PCO; input = qmi_message_wds_get_current_settings_input_new (); qmi_message_wds_get_current_settings_input_set_requested_settings (input, requested, NULL); @@ -952,6 +1116,87 @@ get_current_settings (GTask *task, QmiClientWds *client) qmi_message_wds_get_current_settings_input_unref (input); } +static void +wds_indication_register_response_ready (QmiClientWds *client, + GAsyncResult *res, + GTask *task) +{ + MMBearerQmi *self; + ConnectContext *ctx; + QmiMessageWdsIndicationRegisterOutput *output; + GError *error = NULL; + + self = g_task_get_source_object (task); + ctx = g_task_get_task_data (task); + output = qmi_client_wds_indication_register_finish (client, res, &error); + + if (!output) { + mm_obj_warn (self, "error: operation failed: %s", error->message); + g_error_free (error); + ctx->step++; + connect_context_step (task); + return; + } + + if (!qmi_message_wds_indication_register_output_get_result (output, &error)) { + mm_obj_warn (self, "error: could not register for indication: %s", error->message); + qmi_message_wds_indication_register_output_unref (output); + g_error_free (error); + ctx->step++; + connect_context_step (task); + return; + } + qmi_message_wds_indication_register_output_unref (output); + if (ctx->running_ipv4) { + mm_obj_dbg (self, "v4 extended ip config indication registered successfully"); + g_assert (ctx->extended_ipv4_config_change_id == 0); + ctx->extended_ipv4_config_change_id = + g_signal_connect (client, + "extended-ip-config", + G_CALLBACK (extended_ip_config_indication_received), + self); + } else { + mm_obj_dbg (self, "v6 extended ip Config indication registered successfully"); + g_assert (ctx->extended_ipv6_config_change_id == 0); + ctx->extended_ipv6_config_change_id = + g_signal_connect (client, + "extended-ip-config", + G_CALLBACK (extended_ip_config_indication_received), + self); + } + ctx->step++; + connect_context_step (task); +} + +static void +register_for_wds_indication (ConnectContext *ctx, + GTask *task) +{ + QmiMessageWdsIndicationRegisterInput *input; + QmiClientWds *client; + MMBearerQmi *self; + + input = qmi_message_wds_indication_register_input_new (); + self = g_task_get_source_object (task); + + if (ctx->running_ipv4) { + client = ctx->client_ipv4; + mm_obj_dbg (self, "registering for wds extended ip V4 info indication"); + } else { + client = ctx->client_ipv6; + mm_obj_dbg (self, "registering for wds extended ip V6 info indication"); + } + qmi_message_wds_indication_register_input_set_report_extended_ip_configuration_change (input, TRUE, NULL); + qmi_client_wds_indication_register ( + client, + input, + 10, + g_task_get_cancellable (task), + (GAsyncReadyCallback) wds_indication_register_response_ready, + task); + qmi_message_wds_indication_register_input_unref (input); +} + static GError * mobile_equipment_error_from_start_network_output (MMBearerQmi *self, QmiMessageWdsStartNetworkOutput *output) @@ -1917,6 +2162,15 @@ connect_context_step (GTask *task) return; } + case CONNECT_STEP_ENABLE_WDS_INDICATIONS_IPV4: + /* If call is connected enable wds indications */ + if (ctx->packet_data_handle_ipv4) { + register_for_wds_indication (ctx, task); + return; + } + ctx->step++; + /* fall through */ + case CONNECT_STEP_GET_CURRENT_SETTINGS_IPV4: /* Retrieve and print IP configuration */ if (ctx->packet_data_handle_ipv4) { @@ -2050,6 +2304,15 @@ connect_context_step (GTask *task) return; } + case CONNECT_STEP_ENABLE_WDS_INDICATIONS_IPV6: + /* If call is connected enable wds indications */ + if (ctx->packet_data_handle_ipv6) { + register_for_wds_indication (ctx, task); + return; + } + ctx->step++; + /* fall through */ + case CONNECT_STEP_GET_CURRENT_SETTINGS_IPV6: /* Retrieve and print IP configuration */ if (ctx->packet_data_handle_ipv6) { @@ -2111,6 +2374,8 @@ connect_context_step (GTask *task) ctx->packet_service_status_ipv4_indication_id = 0; ctx->self->priv->event_report_ipv4_indication_id = ctx->event_report_ipv4_indication_id; ctx->event_report_ipv4_indication_id = 0; + ctx->self->priv->extended_ipv4_config_change_id = ctx->extended_ipv4_config_change_id; + ctx->extended_ipv4_config_change_id = 0; ctx->self->priv->client_ipv4 = g_object_ref (ctx->client_ipv4); } @@ -2123,6 +2388,8 @@ connect_context_step (GTask *task) ctx->packet_service_status_ipv6_indication_id = 0; ctx->self->priv->event_report_ipv6_indication_id = ctx->event_report_ipv6_indication_id; ctx->event_report_ipv6_indication_id = 0; + ctx->self->priv->extended_ipv6_config_change_id = ctx->extended_ipv6_config_change_id; + ctx->extended_ipv6_config_change_id = 0; ctx->self->priv->client_ipv6 = g_object_ref (ctx->client_ipv6); } @@ -2507,6 +2774,10 @@ disconnect_context_step (GTask *task) ctx->client_ipv4, &self->priv->event_report_ipv4_indication_id); + if (self->priv->extended_ipv4_config_change_id) { + g_signal_handler_disconnect (ctx->client_ipv4, self->priv->extended_ipv4_config_change_id); + self->priv->extended_ipv4_config_change_id = 0; + } input = qmi_message_wds_stop_network_input_new (); qmi_message_wds_stop_network_input_set_packet_data_handle (input, ctx->packet_data_handle_ipv4, NULL); @@ -2538,6 +2809,10 @@ disconnect_context_step (GTask *task) ctx->client_ipv6, &self->priv->event_report_ipv6_indication_id); + if (self->priv->extended_ipv6_config_change_id) { + g_signal_handler_disconnect (ctx->client_ipv6, self->priv->extended_ipv6_config_change_id); + self->priv->extended_ipv6_config_change_id = 0; + } input = qmi_message_wds_stop_network_input_new (); qmi_message_wds_stop_network_input_set_packet_data_handle (input, ctx->packet_data_handle_ipv6, NULL); @@ -2674,7 +2949,8 @@ dispose (GObject *object) g_assert (!self->priv->ongoing_connect_user_cancellable); g_assert (!self->priv->ongoing_connect_network_cancellable); reset_bearer_connection (self, TRUE, TRUE); - + g_list_free_full (self->priv->pco_list, g_object_unref); + self->priv->pco_list = NULL; G_OBJECT_CLASS (mm_bearer_qmi_parent_class)->dispose (object); } |