aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/mmcli-modem-signal.c4
-rw-r--r--cli/mmcli-output.c1
-rw-r--r--cli/mmcli-output.h1
-rw-r--r--src/mm-broadband-modem-mbim.c122
-rw-r--r--src/mm-modem-helpers-mbim.c85
-rw-r--r--src/mm-modem-helpers-mbim.h17
-rw-r--r--src/tests/meson.build4
-rw-r--r--src/tests/test-modem-helpers-mbim.c198
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, &lte, &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, &lte, &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,
+ &lte,
+ &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, &lte, &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 ();
+}