From 19c817cfe93335aab2c579bb2a75f3336e36b1bd Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Sun, 30 Mar 2025 01:00:52 -0500 Subject: broadband-modem-mbim: consistently handle custom data classes Consistently interpret the modem's reported custom data class when loading current mode, supported mode, supported capabilities, setting current mode, and current access technology. Normalize the data class to include any custom data class immediately after reading it from any MBIM message. De-normalize it when sending back to the modem when setting current modes so the modem receives MBIM_DATA_CLASS_CUSTOM for the custom mode. Fixes: https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/issues/937 Signed-off-by: Dan Williams --- src/mm-broadband-modem-mbim.c | 87 ++++++++++++++++++++++++++++++------------- src/mm-modem-helpers-mbim.c | 85 ++++++++++++++++++++++++++++++++++-------- src/mm-modem-helpers-mbim.h | 17 ++++++--- 3 files changed, 144 insertions(+), 45 deletions(-) (limited to 'src') diff --git a/src/mm-broadband-modem-mbim.c b/src/mm-broadband-modem-mbim.c index 7185a059..4f49a53e 100644 --- a/src/mm-broadband-modem-mbim.c +++ b/src/mm-broadband-modem-mbim.c @@ -130,7 +130,7 @@ struct _MMBroadbandModemMbimPrivate { /* Queried and cached capabilities */ MbimCellularClass caps_cellular_class; MbimDataClass caps_data_class; - gchar *caps_custom_data_class; + MbimDataClass caps_custom_data_class; MbimSmsCaps caps_sms; guint caps_max_sessions; gchar *caps_device_id; @@ -518,6 +518,8 @@ device_caps_query_ready (MbimDevice *device, MMBroadbandModemMbim *self; GError *error = NULL; LoadCurrentCapabilitiesContext *ctx; + MbimDataClass caps_data_class = MBIM_DATA_CLASS_NONE; + g_autofree gchar *caps_custom_data_class_str; self = g_task_get_source_object (task); ctx = g_task_get_task_data (task); @@ -550,7 +552,7 @@ device_caps_query_ready (MbimDevice *device, NULL, /* lte_band_class_array */ NULL, /* nr_band_class_array_size */ NULL, /* nr_band_class_array */ - &self->priv->caps_custom_data_class, + &caps_custom_data_class_str, &self->priv->caps_device_id, &self->priv->caps_firmware_info, &self->priv->caps_hardware_info, @@ -560,7 +562,7 @@ device_caps_query_ready (MbimDevice *device, return; } /* Translate data class v3 to standard data class to simplify further usage of the field */ - self->priv->caps_data_class = mm_mbim_data_class_from_mbim_data_class_v3_and_subclass (data_class_v3, data_subclass); + caps_data_class = mm_mbim_data_class_from_mbim_data_class_v3_and_subclass (data_class_v3, data_subclass); } else if (mbim_device_check_ms_mbimex_version (device, 2, 0)) { if (!mbim_message_ms_basic_connect_extensions_device_caps_response_parse ( response, @@ -568,11 +570,11 @@ device_caps_query_ready (MbimDevice *device, &self->priv->caps_cellular_class, NULL, /* voice_class */ NULL, /* sim_class */ - &self->priv->caps_data_class, + &caps_data_class, &self->priv->caps_sms, NULL, /* ctrl_caps */ &self->priv->caps_max_sessions, - &self->priv->caps_custom_data_class, + &caps_custom_data_class_str, &self->priv->caps_device_id, &self->priv->caps_firmware_info, &self->priv->caps_hardware_info, @@ -589,11 +591,11 @@ device_caps_query_ready (MbimDevice *device, &self->priv->caps_cellular_class, NULL, /* voice_class */ NULL, /* sim_class */ - &self->priv->caps_data_class, + &caps_data_class, &self->priv->caps_sms, NULL, /* ctrl_caps */ &self->priv->caps_max_sessions, - &self->priv->caps_custom_data_class, + &caps_custom_data_class_str, &self->priv->caps_device_id, &self->priv->caps_firmware_info, &self->priv->caps_hardware_info, @@ -604,9 +606,14 @@ device_caps_query_ready (MbimDevice *device, } } + /* Normalize data class capabilities to include any custom data class */ + self->priv->caps_custom_data_class = mm_mbim_data_class_from_custom_caps (caps_data_class, + caps_custom_data_class_str); + self->priv->caps_data_class = mm_modem_mbim_normalize_data_class_mask (caps_data_class, + self->priv->caps_custom_data_class); + ctx->current_mbim = mm_modem_capability_from_mbim_device_caps (self->priv->caps_cellular_class, - self->priv->caps_data_class, - self->priv->caps_custom_data_class); + self->priv->caps_data_class); complete_current_capabilities (task); } @@ -708,8 +715,7 @@ load_supported_capabilities_mbim (GTask *task) /* Current capabilities should have been cached already, just assume them */ current = mm_modem_capability_from_mbim_device_caps (self->priv->caps_cellular_class, - self->priv->caps_data_class, - self->priv->caps_custom_data_class); + self->priv->caps_data_class); if (current != 0) { supported = g_array_sized_new (FALSE, FALSE, sizeof (MMModemCapability), 1); g_array_append_val (supported, current); @@ -1067,7 +1073,7 @@ load_supported_modes_mbim (GTask *task, } /* Build all */ - mask_all = mm_modem_mode_from_mbim_data_class (self->priv->caps_data_class, self->priv->caps_custom_data_class); + mask_all = mm_modem_mode_from_mbim_data_class (self->priv->caps_data_class); mode.allowed = mask_all; mode.preferred = MM_MODEM_MODE_NONE; all = g_array_sized_new (FALSE, FALSE, sizeof (MMModemModeCombination), 1); @@ -1188,11 +1194,14 @@ register_state_current_modes_query_ready (MbimDevice *device, GAsyncResult *res, GTask *task) { + MMBroadbandModemMbim *self; g_autoptr(MbimMessage) response = NULL; MMModemModeCombination *mode = NULL; GError *error = NULL; MbimDataClass preferred_data_classes; + self = g_task_get_source_object (task); + response = mbim_device_command_finish (device, res, &error); if (!response || !mbim_message_response_get_result (response, MBIM_MESSAGE_TYPE_COMMAND_DONE, &error) || @@ -1214,8 +1223,12 @@ register_state_current_modes_query_ready (MbimDevice *device, return; } + /* Normalize preferred data class to include any custom data class */ + preferred_data_classes = mm_modem_mbim_normalize_data_class_mask (preferred_data_classes, + self->priv->caps_custom_data_class); + mode = g_new0 (MMModemModeCombination, 1); - mode->allowed = mm_modem_mode_from_mbim_data_class (preferred_data_classes, NULL); + mode->allowed = mm_modem_mode_from_mbim_data_class (preferred_data_classes); mode->preferred = MM_MODEM_MODE_NONE; g_task_return_pointer (task, mode, (GDestroyNotify)g_free); g_object_unref (task); @@ -1301,9 +1314,17 @@ complete_pending_allowed_modes_action (MMBroadbandModemMbim *self, if (!self->priv->pending_allowed_modes_action) return; + /* requested_data_classes is de-normalized (since we just sent it to the modem) */ requested_data_classes = (MbimDataClass) GPOINTER_TO_UINT (g_task_get_task_data (self->priv->pending_allowed_modes_action)); - requested_modes = mm_modem_mode_from_mbim_data_class (requested_data_classes, NULL); - preferred_modes = mm_modem_mode_from_mbim_data_class (preferred_data_classes, NULL); + requested_modes = mm_modem_mode_from_mbim_data_class (requested_data_classes); + + /* But preferred_data_classes is normalized (since we just pulled it out of + * an MBIM message). De-normalize preferred_data_classes so we can compare + * it to requested_data_classes. + */ + preferred_data_classes = mm_modem_mbim_normalize_data_class_mask (preferred_data_classes, + self->priv->caps_custom_data_class); + preferred_modes = mm_modem_mode_from_mbim_data_class (preferred_data_classes); /* only early complete on success, as we don't know if they're going to be * intermediate indications emitted before the preference change is valid */ @@ -1367,8 +1388,8 @@ register_state_current_modes_set_ready (MbimDevice *device, return; } - requested_modes = mm_modem_mode_from_mbim_data_class (requested_data_classes, NULL); - preferred_modes = mm_modem_mode_from_mbim_data_class (preferred_data_classes, NULL); + requested_modes = mm_modem_mode_from_mbim_data_class (requested_data_classes); + preferred_modes = mm_modem_mode_from_mbim_data_class (preferred_data_classes); if (requested_modes != preferred_modes) { g_autofree gchar *requested_modes_str = NULL; @@ -1422,6 +1443,7 @@ modem_set_current_modes (MMIfaceModem *_self, GTask *task; MbimDevice *device; g_autoptr(GCancellable) cancellable = NULL; + MbimDataClass normalized_class; if (!peek_device (self, &device, callback, user_data)) return; @@ -1445,11 +1467,17 @@ modem_set_current_modes (MMIfaceModem *_self, /* Limit ANY to the currently supported modes */ if (allowed == MM_MODEM_MODE_ANY) - allowed = mm_modem_mode_from_mbim_data_class (self->priv->caps_data_class, self->priv->caps_custom_data_class); + allowed = mm_modem_mode_from_mbim_data_class (self->priv->caps_data_class); + + normalized_class = mm_mbim_data_class_from_modem_mode (allowed, + mm_iface_modem_is_3gpp (_self), + mm_iface_modem_is_cdma (_self)); - self->priv->requested_data_class = mm_mbim_data_class_from_modem_mode (allowed, - mm_iface_modem_is_3gpp (_self), - mm_iface_modem_is_cdma (_self)); + /* Replace any normalized data class with MBIM_DATA_CLASS_CUSTOM + * before sending back to the modem. + */ + self->priv->requested_data_class = mm_modem_mbim_denormalize_data_class_mask (normalized_class, + self->priv->caps_custom_data_class); /* Store the ongoing allowed modes action, so that we can finish the * operation early via indications, instead of waiting for the modem @@ -5271,6 +5299,13 @@ common_process_register_state (MMBroadbandModemMbim *self, } } + /* Normalize preferred and available data classes to include any custom data class */ + preferred_data_classes = mm_modem_mbim_normalize_data_class_mask (preferred_data_classes, + self->priv->caps_custom_data_class); + available_data_classes = mm_modem_mbim_normalize_data_class_mask (available_data_classes, + self->priv->caps_custom_data_class); + + nw_error = mm_broadband_modem_mbim_normalize_nw_error (self, nw_error); nw_error_str = mbim_nw_error_get_string (nw_error); available_data_classes_str = mbim_data_class_build_string_from_mask (available_data_classes); @@ -5753,9 +5788,12 @@ common_process_packet_service (MMBroadbandModemMbim *self, if (packet_service_state == MBIM_PACKET_SERVICE_STATE_ATTACHED) { if (data_class_v3) - self->priv->enabled_cache.highest_available_data_class = mm_mbim_data_class_from_mbim_data_class_v3_and_subclass (data_class_v3, data_subclass); - else - self->priv->enabled_cache.highest_available_data_class = data_class; + data_class = mm_mbim_data_class_from_mbim_data_class_v3_and_subclass (data_class_v3, data_subclass); + + /* Normalize data class to include any custom data class */ + data_class = mm_modem_mbim_normalize_data_class_mask (data_class, + self->priv->caps_custom_data_class); + self->priv->enabled_cache.highest_available_data_class = data_class; } else if (packet_service_state == MBIM_PACKET_SERVICE_STATE_DETACHED) { self->priv->enabled_cache.highest_available_data_class = 0; } @@ -10393,7 +10431,6 @@ finalize (GObject *object) { MMBroadbandModemMbim *self = MM_BROADBAND_MODEM_MBIM (object); - g_free (self->priv->caps_custom_data_class); g_free (self->priv->caps_device_id); g_free (self->priv->caps_firmware_info); g_free (self->priv->caps_hardware_info); diff --git a/src/mm-modem-helpers-mbim.c b/src/mm-modem-helpers-mbim.c index 49d38531..aef31866 100644 --- a/src/mm-modem-helpers-mbim.c +++ b/src/mm-modem-helpers-mbim.c @@ -25,10 +25,77 @@ /*****************************************************************************/ +typedef struct { + const gchar *custom_class; + const MbimDataClass primary_data_class; + const MbimDataClass secondary_data_class; +} CustomDataClass; + +static const CustomDataClass custom_data_classes[] = { + /* "5GSA/TDS": Quectel RM502Q */ + { "5GSA", MBIM_DATA_CLASS_5G_SA, MBIM_DATA_CLASS_NONE }, + /* "5G/TDS": Telit FN990, Quectel RM502Q, Gosuncn GM800 + * "5G": Dell Snapdragon X55/Foxconn T99W175 + */ + { "5G", MBIM_DATA_CLASS_5G_NSA, MBIM_DATA_CLASS_5G_SA }, + /* "HSPA+": Dell DW5821e/Foxconn T77W968, Huawei EM820W */ + { "HSPA+", MBIM_DATA_CLASS_HSDPA | MBIM_DATA_CLASS_HSUPA, MBIM_DATA_CLASS_NONE }, +}; + +MbimDataClass +mm_mbim_data_class_from_custom_caps (MbimDataClass orig_data_class, + const gchar *custom_data_class) +{ + guint i; + + if (orig_data_class & MBIM_DATA_CLASS_CUSTOM) { + for (i = 0; i < G_N_ELEMENTS (custom_data_classes); i++) { + if (strstr (custom_data_class, custom_data_classes[i].custom_class)) { + /* If the original data class already includes the primary custom + * class add the secondary instead. Devices sometimes report a variant + * of the custom class depending on MBIMex version. + * + * For example, Foxconn X55 supports SA but reports "...,lte,custom" + * with MBIMex1 and "...,lte,5g-nsa,custom" with MBIMex2. Fix that + * up with the highest level we can be sure the device supports. + */ + if (orig_data_class & custom_data_classes[i].primary_data_class) + return custom_data_classes[i].secondary_data_class; + else + return custom_data_classes[i].primary_data_class; + } + } + } + return MBIM_DATA_CLASS_NONE; +} + +MbimDataClass +mm_modem_mbim_normalize_data_class_mask (MbimDataClass orig_data_class, + MbimDataClass custom_data_class) +{ + if (orig_data_class & MBIM_DATA_CLASS_CUSTOM) { + orig_data_class |= custom_data_class; + orig_data_class &= ~MBIM_DATA_CLASS_CUSTOM; + } + return orig_data_class; +} + +MbimDataClass +mm_modem_mbim_denormalize_data_class_mask (MbimDataClass orig_data_class, + MbimDataClass custom_data_class) +{ + if (orig_data_class & custom_data_class) { + orig_data_class &= ~custom_data_class; + orig_data_class |= MBIM_DATA_CLASS_CUSTOM; + } + return orig_data_class; +} + +/*****************************************************************************/ + MMModemCapability mm_modem_capability_from_mbim_device_caps (MbimCellularClass caps_cellular_class, - MbimDataClass caps_data_class, - const gchar *caps_custom_data_class) + MbimDataClass caps_data_class) { MMModemCapability mask = 0; @@ -43,12 +110,6 @@ mm_modem_capability_from_mbim_device_caps (MbimCellularClass caps_cellular_clas if (caps_data_class & MBIM_DATA_CLASS_LTE) mask |= MM_MODEM_CAPABILITY_LTE; - /* e.g. Gosuncn GM800 reports MBIM custom data class "5G/TDS" */ - if ((caps_data_class & MBIM_DATA_CLASS_CUSTOM) && caps_custom_data_class) { - if (strstr (caps_custom_data_class, "5G")) - mask |= MM_MODEM_CAPABILITY_5GNR; - } - /* Support for devices with Microsoft extensions */ if (caps_data_class & (MBIM_DATA_CLASS_5G_NSA | MBIM_DATA_CLASS_5G_SA)) mask |= MM_MODEM_CAPABILITY_5GNR; @@ -148,8 +209,7 @@ mm_modem_3gpp_packet_service_state_from_mbim_packet_service_state (MbimPacketSer /*****************************************************************************/ MMModemMode -mm_modem_mode_from_mbim_data_class (MbimDataClass data_class, - const gchar *caps_custom_data_class) +mm_modem_mode_from_mbim_data_class (MbimDataClass data_class) { MMModemMode mask = MM_MODEM_MODE_NONE; @@ -166,11 +226,6 @@ mm_modem_mode_from_mbim_data_class (MbimDataClass data_class, if (data_class & (MBIM_DATA_CLASS_5G_NSA | MBIM_DATA_CLASS_5G_SA)) mask |= MM_MODEM_MODE_5G; - /* Some modems (e.g. Telit FN990) reports MBIM custom data class "5G/TDS" */ - if ((data_class & MBIM_DATA_CLASS_CUSTOM) && caps_custom_data_class) { - if (strstr (caps_custom_data_class, "5G")) - mask |= MM_MODEM_MODE_5G; - } /* 3GPP2... */ if (data_class & MBIM_DATA_CLASS_1XRTT) diff --git a/src/mm-modem-helpers-mbim.h b/src/mm-modem-helpers-mbim.h index ba664bbb..6a058937 100644 --- a/src/mm-modem-helpers-mbim.h +++ b/src/mm-modem-helpers-mbim.h @@ -27,9 +27,17 @@ /*****************************************************************************/ /* MBIM/BasicConnect to MM translations */ -MMModemCapability mm_modem_capability_from_mbim_device_caps (MbimCellularClass caps_cellular_class, - MbimDataClass caps_data_class, - const gchar *caps_custom_data_class); +MbimDataClass mm_mbim_data_class_from_custom_caps (MbimDataClass orig_data_class, + const gchar *custom_data_class); + +MbimDataClass mm_modem_mbim_normalize_data_class_mask (MbimDataClass orig_data_class, + MbimDataClass custom_data_class); + +MbimDataClass mm_modem_mbim_denormalize_data_class_mask (MbimDataClass orig_data_class, + MbimDataClass custom_data_class); + +MMModemCapability mm_modem_capability_from_mbim_device_caps (MbimCellularClass caps_cellular_class, + MbimDataClass caps_data_class); MMModemLock mm_modem_lock_from_mbim_pin_type (MbimPinType pin_type); @@ -40,8 +48,7 @@ MMModem3gppPacketServiceState mm_modem_3gpp_packet_service_state_from_mbim_packe MbimDataClass mm_mbim_data_class_from_mbim_data_class_v3_and_subclass (MbimDataClassV3 data_class_v3, MbimDataSubclass data_subclass); -MMModemMode mm_modem_mode_from_mbim_data_class (MbimDataClass data_class, - const gchar *caps_custom_data_class); +MMModemMode mm_modem_mode_from_mbim_data_class (MbimDataClass data_class); MbimDataClass mm_mbim_data_class_from_modem_mode (MMModemMode modem_mode, gboolean is_3gpp, -- cgit v1.2.3-70-g09d2 From a420972e9b51ea543d2b6886ae1afabf7f5f8e4e Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 1 Apr 2025 09:51:45 -0500 Subject: broadband-modem-mbim: consolidate determination of data class Do it in a function instead of the same thing in a bunch of places. Signed-off-by: Dan Williams --- src/mm-broadband-modem-mbim.c | 35 ++++++++++++++++++----------------- 1 file changed, 18 insertions(+), 17 deletions(-) (limited to 'src') diff --git a/src/mm-broadband-modem-mbim.c b/src/mm-broadband-modem-mbim.c index 4f49a53e..3df43903 100644 --- a/src/mm-broadband-modem-mbim.c +++ b/src/mm-broadband-modem-mbim.c @@ -2211,6 +2211,18 @@ modem_power_down (MMIfaceModem *self, /*****************************************************************************/ /* Signal quality loading (Modem interface) */ +static MbimDataClass +enabled_cache_best_available_data_class (MMBroadbandModemMbim *self) +{ + MbimDataClass data_class; + + /* Best guess of current data class */ + data_class = self->priv->enabled_cache.highest_available_data_class; + if (data_class == 0) + data_class = self->priv->enabled_cache.available_data_classes; + return data_class; +} + static guint modem_load_signal_quality_finish (MMIfaceModem *self, GAsyncResult *res, @@ -2283,10 +2295,7 @@ signal_state_query_ready (MbimDevice *device, if (error) g_task_return_error (task, error); else { - /* Best guess of current data class */ - data_class = self->priv->enabled_cache.highest_available_data_class; - if (data_class == 0) - data_class = self->priv->enabled_cache.available_data_classes; + data_class = enabled_cache_best_available_data_class (self); if (mm_signal_from_mbim_signal_state (data_class, rssi, error_rate, rsrp_snr, rsrp_snr_count, self, &cdma, &evdo, &gsm, &umts, <e, &nr5g)) mm_iface_modem_signal_update (MM_IFACE_MODEM_SIGNAL (self), cdma, evdo, gsm, umts, lte, nr5g); @@ -4942,11 +4951,7 @@ basic_connect_notification_signal_state (MMBroadbandModemMbim *self, quality = mm_signal_quality_from_mbim_signal_state (coded_rssi, rsrp_snr, rsrp_snr_count, self); mm_iface_modem_update_signal_quality (MM_IFACE_MODEM (self), quality); - /* Best guess of current data class */ - data_class = self->priv->enabled_cache.highest_available_data_class; - if (data_class == 0) - data_class = self->priv->enabled_cache.available_data_classes; - + data_class = enabled_cache_best_available_data_class (self); if (mm_signal_from_mbim_signal_state (data_class, coded_rssi, coded_error_rate, rsrp_snr, rsrp_snr_count, self, &cdma, &evdo, &gsm, &umts, <e, &nr5g)) mm_iface_modem_signal_update (MM_IFACE_MODEM_SIGNAL (self), cdma, evdo, gsm, umts, lte, nr5g); @@ -5009,11 +5014,10 @@ static void update_access_technologies (MMBroadbandModemMbim *self) { MMModemAccessTechnology act; + MbimDataClass data_class; - act = mm_modem_access_technology_from_mbim_data_class (self->priv->enabled_cache.highest_available_data_class); - if (act == MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN) - act = mm_modem_access_technology_from_mbim_data_class (self->priv->enabled_cache.available_data_classes); - + data_class = enabled_cache_best_available_data_class (self); + act = mm_modem_access_technology_from_mbim_data_class (data_class); mm_iface_modem_3gpp_update_access_technologies (MM_IFACE_MODEM_3GPP (self), act); } @@ -7570,10 +7574,7 @@ mbimexv2_signal_state_query_ready (MbimDevice *device, result = g_slice_new0 (SignalLoadValuesResult); - /* Best guess of current data class */ - data_class = self->priv->enabled_cache.highest_available_data_class; - if (data_class == 0) - data_class = self->priv->enabled_cache.available_data_classes; + data_class = enabled_cache_best_available_data_class (self); if (!mm_signal_from_mbim_signal_state ( data_class, rssi, error_rate, rsrp_snr, rsrp_snr_count, self, NULL, NULL, &result->gsm, &result->umts, &result->lte, &result->nr5g)) { -- cgit v1.2.3-70-g09d2 From ceb1cd9a99dcf67860af85e0d6fee2badc362a39 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Tue, 1 Apr 2025 09:53:14 -0500 Subject: tests: add tests for mm_signal_from_mbim_signal_state() Signed-off-by: Dan Williams --- src/tests/meson.build | 4 + src/tests/test-modem-helpers-mbim.c | 198 ++++++++++++++++++++++++++++++++++++ 2 files changed, 202 insertions(+) create mode 100644 src/tests/test-modem-helpers-mbim.c (limited to 'src') diff --git a/src/tests/meson.build b/src/tests/meson.build index b303ff5d..a6a1ae72 100644 --- a/src/tests/meson.build +++ b/src/tests/meson.build @@ -25,6 +25,10 @@ if enable_qmi test_units += {'modem-helpers-qmi': libkerneldevice_dep} endif +if enable_mbim + test_units += {'modem-helpers-mbim': libkerneldevice_dep} +endif + foreach test_unit, test_deps: test_units test_name = 'test-' + test_unit diff --git a/src/tests/test-modem-helpers-mbim.c b/src/tests/test-modem-helpers-mbim.c new file mode 100644 index 00000000..b84664db --- /dev/null +++ b/src/tests/test-modem-helpers-mbim.c @@ -0,0 +1,198 @@ +/* -*- 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) 2025 Dan Williams + */ + +#include +#include +#include +#include +#include + +#include +#define _LIBMM_INSIDE_MM +#include + +#include "mm-enums-types.h" +#include "mm-flags-types.h" +#include "mm-modem-helpers-mbim.h" +#include "mm-log-test.h" + +/*****************************************************************************/ + +typedef struct { + const MbimDataClass data_class; + const gdouble rssi; + const gdouble rscp; + const gdouble ecio; + const gdouble sinr; + const gdouble io; + const gdouble rsrq; + const gdouble rsrp; + const gdouble snr; + const gdouble error_rate; +} ExpectedSignal; + +typedef struct { + const gchar *detail; + const gboolean expect_success; + + const MbimDataClass data_class; + const guint coded_rssi; + const guint coded_error_rate; + const MbimRsrpSnrInfo rsrp_snr[2]; + + const ExpectedSignal expected[2]; +} SignalStateTestcase; + +const SignalStateTestcase signal_tests[] = { + { "5g-no-rsrp-snr", + TRUE, + MBIM_DATA_CLASS_5G_SA, + 16, 99, + { { .system_type = MBIM_DATA_CLASS_NONE }, { .system_type = MBIM_DATA_CLASS_NONE } }, + { { MBIM_DATA_CLASS_5G_SA, + -97, + MM_SIGNAL_UNKNOWN, + MM_SIGNAL_UNKNOWN, + MM_SIGNAL_UNKNOWN, + MM_SIGNAL_UNKNOWN, + MM_SIGNAL_UNKNOWN, + MM_SIGNAL_UNKNOWN, + MM_SIGNAL_UNKNOWN, + MM_SIGNAL_UNKNOWN, + }, + { MBIM_DATA_CLASS_NONE } + }, + }, +}; + +#if 0 +typedef struct { + guint32 rsrp; + guint32 snr; + guint32 rsrp_threshold; + guint32 snr_threshold; + guint32 system_type; +} MbimRsrpSnrInfo; +#endif + +static MMSignal * +select_signal_for_data_class (MbimDataClass data_class, + MMSignal **cdma, + MMSignal **evdo, + MMSignal **gsm, + MMSignal **umts, + MMSignal **lte, + MMSignal **nr5g) +{ + if (data_class & (MBIM_DATA_CLASS_5G_NSA | + MBIM_DATA_CLASS_5G_SA)) + return *nr5g; + if (data_class & (MBIM_DATA_CLASS_LTE)) + return *lte; + if (data_class & (MBIM_DATA_CLASS_UMTS | + MBIM_DATA_CLASS_HSDPA | + MBIM_DATA_CLASS_HSUPA)) + return *umts; + if (data_class & (MBIM_DATA_CLASS_GPRS | + MBIM_DATA_CLASS_EDGE)) + return *gsm; + if (data_class & (MBIM_DATA_CLASS_1XEVDO | + MBIM_DATA_CLASS_1XEVDO_REVA | + MBIM_DATA_CLASS_1XEVDV | + MBIM_DATA_CLASS_3XRTT | + MBIM_DATA_CLASS_1XEVDO_REVB)) + return *evdo; + if (data_class & MBIM_DATA_CLASS_1XRTT) + return *cdma; + return NULL; +} + +static void +test_signal_state_case (gconstpointer user_data) +{ + const SignalStateTestcase *tc = user_data; + gboolean success; + guint count; + g_autoptr(MMSignal) cdma; + g_autoptr(MMSignal) evdo; + g_autoptr(MMSignal) gsm; + g_autoptr(MMSignal) umts; + g_autoptr(MMSignal) lte; + g_autoptr(MMSignal) nr5g; + MMSignal *tmp; + + for (count = 0; + count < G_N_ELEMENTS (tc->rsrp_snr) && tc->rsrp_snr[count].system_type; + count++); + + success = mm_signal_from_mbim_signal_state (tc->data_class, + tc->coded_rssi, + tc->coded_error_rate, + (MbimRsrpSnrInfo **) &tc->rsrp_snr, + count, + NULL, + &cdma, + &evdo, + &gsm, + &umts, + <e, + &nr5g); + g_assert_cmpint (success, ==, tc->expect_success); + + for (count = 0; + count < G_N_ELEMENTS (tc->expected) && (tc->expected[count].data_class != MBIM_DATA_CLASS_NONE); + count++) { + tmp = select_signal_for_data_class (tc->expected[count].data_class, + &cdma, &evdo, &gsm, &umts, <e, &nr5g); + g_assert (tmp); + g_assert_cmpfloat (mm_signal_get_rssi (tmp), ==, tc->expected[count].rssi); + g_assert_cmpfloat (mm_signal_get_rscp (tmp), ==, tc->expected[count].rscp); + g_assert_cmpfloat (mm_signal_get_ecio (tmp), ==, tc->expected[count].ecio); + g_assert_cmpfloat (mm_signal_get_sinr (tmp), ==, tc->expected[count].sinr); + g_assert_cmpfloat (mm_signal_get_io (tmp), ==, tc->expected[count].io); + g_assert_cmpfloat (mm_signal_get_rsrq (tmp), ==, tc->expected[count].rsrq); + g_assert_cmpfloat (mm_signal_get_rsrp (tmp), ==, tc->expected[count].rsrp); + g_assert_cmpfloat (mm_signal_get_snr (tmp), ==, tc->expected[count].snr); + g_assert_cmpfloat (mm_signal_get_error_rate (tmp), ==, tc->expected[count].error_rate); + } + if (count == 0 ) { + g_assert (!cdma); + g_assert (!evdo); + g_assert (!gsm); + g_assert (!umts); + g_assert (!lte); + g_assert (!nr5g); + } +} + +/*****************************************************************************/ + +int main (int argc, char **argv) +{ + guint i; + + setlocale (LC_ALL, ""); + + g_test_init (&argc, &argv, NULL); + + for (i = 0; i < G_N_ELEMENTS (signal_tests); i++) { + g_autofree gchar *detail; + + detail = g_strdup_printf ("/MM/mbim/signal-state/%s", signal_tests[i].detail); + g_test_add_data_func (detail, &signal_tests[i], test_signal_state_case); + } + + return g_test_run (); +} -- cgit v1.2.3-70-g09d2