diff options
-rw-r--r-- | cli/mmcli-modem-signal.c | 4 | ||||
-rw-r--r-- | cli/mmcli-output.c | 1 | ||||
-rw-r--r-- | cli/mmcli-output.h | 1 | ||||
-rw-r--r-- | src/mm-broadband-modem-mbim.c | 122 | ||||
-rw-r--r-- | src/mm-modem-helpers-mbim.c | 85 | ||||
-rw-r--r-- | src/mm-modem-helpers-mbim.h | 17 | ||||
-rw-r--r-- | src/tests/meson.build | 4 | ||||
-rw-r--r-- | src/tests/test-modem-helpers-mbim.c | 198 |
8 files changed, 370 insertions, 62 deletions
diff --git a/cli/mmcli-modem-signal.c b/cli/mmcli-modem-signal.c index 7d1e2ff6..ce23e7c1 100644 --- a/cli/mmcli-modem-signal.c +++ b/cli/mmcli-modem-signal.c @@ -166,6 +166,7 @@ print_signal_info (void) gchar *lte_rsrq = NULL; gchar *lte_snr = NULL; gchar *lte_error_rate = NULL; + gchar *nr5g_rssi = NULL; gchar *nr5g_rsrp = NULL; gchar *nr5g_rsrq = NULL; gchar *nr5g_snr = NULL; @@ -235,6 +236,8 @@ print_signal_info (void) signal = mm_modem_signal_peek_nr5g (ctx->modem_signal); if (signal) { + if ((value = mm_signal_get_rssi (signal)) != MM_SIGNAL_UNKNOWN) + nr5g_rssi = g_strdup_printf ("%.2lf", value); if ((value = mm_signal_get_rsrq (signal)) != MM_SIGNAL_UNKNOWN) nr5g_rsrq = g_strdup_printf ("%.2lf", value); if ((value = mm_signal_get_rsrp (signal)) != MM_SIGNAL_UNKNOWN) @@ -267,6 +270,7 @@ print_signal_info (void) mmcli_output_string_take_typed (MMC_F_SIGNAL_LTE_RSRP, lte_rsrp, "dBm"); mmcli_output_string_take_typed (MMC_F_SIGNAL_LTE_SNR, lte_snr, "dB"); mmcli_output_string_take_typed (MMC_F_SIGNAL_LTE_ERROR_RATE, lte_error_rate, "%%"); + mmcli_output_string_take_typed (MMC_F_SIGNAL_5G_RSSI, nr5g_rssi, "dBm"); mmcli_output_string_take_typed (MMC_F_SIGNAL_5G_RSRQ, nr5g_rsrq, "dB"); mmcli_output_string_take_typed (MMC_F_SIGNAL_5G_RSRP, nr5g_rsrp, "dBm"); mmcli_output_string_take_typed (MMC_F_SIGNAL_5G_SNR, nr5g_snr, "dB"); diff --git a/cli/mmcli-output.c b/cli/mmcli-output.c index 440d6059..c5a10b84 100644 --- a/cli/mmcli-output.c +++ b/cli/mmcli-output.c @@ -202,6 +202,7 @@ static FieldInfo field_infos[] = { [MMC_F_SIGNAL_LTE_RSRP] = { "modem.signal.lte.rsrp", "rsrp", MMC_S_MODEM_SIGNAL_LTE, }, [MMC_F_SIGNAL_LTE_SNR] = { "modem.signal.lte.snr", "s/n", MMC_S_MODEM_SIGNAL_LTE, }, [MMC_F_SIGNAL_LTE_ERROR_RATE] = { "modem.signal.lte.error-rate", "error rate", MMC_S_MODEM_SIGNAL_LTE, }, + [MMC_F_SIGNAL_5G_RSSI] = { "modem.signal.5g.rssi", "rssi", MMC_S_MODEM_SIGNAL_5G, }, [MMC_F_SIGNAL_5G_RSRQ] = { "modem.signal.5g.rsrq", "rsrq", MMC_S_MODEM_SIGNAL_5G, }, [MMC_F_SIGNAL_5G_RSRP] = { "modem.signal.5g.rsrp", "rsrp", MMC_S_MODEM_SIGNAL_5G, }, [MMC_F_SIGNAL_5G_SNR] = { "modem.signal.5g.snr", "s/n", MMC_S_MODEM_SIGNAL_5G, }, diff --git a/cli/mmcli-output.h b/cli/mmcli-output.h index a9f050d8..700b6800 100644 --- a/cli/mmcli-output.h +++ b/cli/mmcli-output.h @@ -214,6 +214,7 @@ typedef enum { MMC_F_SIGNAL_LTE_RSRP, MMC_F_SIGNAL_LTE_SNR, MMC_F_SIGNAL_LTE_ERROR_RATE, + MMC_F_SIGNAL_5G_RSSI, MMC_F_SIGNAL_5G_RSRQ, MMC_F_SIGNAL_5G_RSRP, MMC_F_SIGNAL_5G_SNR, diff --git a/src/mm-broadband-modem-mbim.c b/src/mm-broadband-modem-mbim.c index 7185a059..3df43903 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 @@ -2183,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, @@ -2255,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); @@ -4914,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); @@ -4981,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); } @@ -5271,6 +5303,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 +5792,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; } @@ -7532,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)) { @@ -10393,7 +10432,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, 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 <dan@ioncontrol.co> + */ + +#include <glib.h> +#include <glib-object.h> +#include <string.h> +#include <stdlib.h> +#include <locale.h> + +#include <ModemManager.h> +#define _LIBMM_INSIDE_MM +#include <libmm-glib.h> + +#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 (); +} |