From f4a1c0fcecd3ccaa8c7ab94d9269a546f3dc1784 Mon Sep 17 00:00:00 2001 From: Florian Eckert Date: Fri, 20 Sep 2024 09:00:14 +0200 Subject: [PATCH 1/5] cinterion: add MV32 to the rules file Add the missing udev rules for the MV32 from 'Telit' which uses the cinterion plugin. Signed-off-by: Florian Eckert --- src/plugins/cinterion/77-mm-cinterion-port-types.rules | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/plugins/cinterion/77-mm-cinterion-port-types.rules b/src/plugins/cinterion/77-mm-cinterion-port-types.rules index 77f3b8ab..0ff2a1ea 100644 --- a/src/plugins/cinterion/77-mm-cinterion-port-types.rules +++ b/src/plugins/cinterion/77-mm-cinterion-port-types.rules @@ -68,4 +68,13 @@ ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="006F", ENV{.MM_USBIFNUM}=="02", SUBS ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="006F", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="006F", ENV{.MM_USBIFNUM}=="06", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_QCDM}="1" +# MV32 port types +# ttyUSB0 (if #2): AT port +# ttyUSB1 (if #4): GPS data port +# ttyUSB2 (if #5): unknown +ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="00f1", ENV{ID_MM_TTY_AT_PROBE_TRIES}="10" +ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="00f1", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" +ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="00f1", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" +ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="00f1", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_PORT_IGNORE}="1" + LABEL="mm_cinterion_port_types_end" From ebebce61112470b5d1d4beb5ed0f871f060e975c Mon Sep 17 00:00:00 2001 From: Florian Eckert Date: Fri, 20 Sep 2024 09:01:04 +0200 Subject: [PATCH 2/5] cinterion: add MV31 to the rules file Add the missing udev rules for the MV31 from 'Telit' which uses the cinterion plugin. Signed-off-by: Florian Eckert --- src/plugins/cinterion/77-mm-cinterion-port-types.rules | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/plugins/cinterion/77-mm-cinterion-port-types.rules b/src/plugins/cinterion/77-mm-cinterion-port-types.rules index 0ff2a1ea..4ef89a1b 100644 --- a/src/plugins/cinterion/77-mm-cinterion-port-types.rules +++ b/src/plugins/cinterion/77-mm-cinterion-port-types.rules @@ -77,4 +77,13 @@ ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="00f1", ENV{.MM_USBIFNUM}=="02", SUBS ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="00f1", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="00f1", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_PORT_IGNORE}="1" +# MV31 port types +# ttyUSB0 (if #2): AT port +# ttyUSB1 (if #4): GPS data port +# ttyUSB2 (if #5): unknown +ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="00b3", ENV{ID_MM_TTY_AT_PROBE_TRIES}="10" +ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="00b3", ENV{.MM_USBIFNUM}=="02", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_AT_PRIMARY}="1" +ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="00b3", ENV{.MM_USBIFNUM}=="04", SUBSYSTEM=="tty", ENV{ID_MM_PORT_TYPE_GPS}="1" +ATTRS{idVendor}=="1e2d", ATTRS{idProduct}=="00b3", ENV{.MM_USBIFNUM}=="05", ENV{ID_MM_PORT_IGNORE}="1" + LABEL="mm_cinterion_port_types_end" From a107de32a856d44d19740fb4907233c54f29cfca Mon Sep 17 00:00:00 2001 From: Florian Eckert Date: Fri, 20 Sep 2024 09:17:57 +0200 Subject: [PATCH 3/5] cinterion: add revision read via AT for the MV31 and MV32 Reading the revsion via the mbim interface returns an incorrect firmware revsion string. This contains a '\n' character so that the revision looks like this in 'mmcli'. Hardware | manufacturer: Cinterion | model: Cinterion PID 0x00F1 USB Mobile Broadband | firmware revision: FDE.F0.3.0.1.1.DT.005 | 062 | carrier config: DT The correct string without '\n' is returned via the AT interface. To fix the error, the 'mbim' revision interface is overwritten with an AT call if a MV31 or a MV32 is detected. So we get the correct output via 'mmcli'. Hardware | manufacturer: Cinterion | model: Cinterion PID 0x00F1 USB Mobile Broadband | firmware revision: FDE.F0.3.0.1.1.DT.005.062 | carrier config: DT Signed-off-by: Florian Eckert --- .../mm-broadband-modem-mbim-cinterion.c | 137 +++++++++++++++++- 1 file changed, 135 insertions(+), 2 deletions(-) diff --git a/src/plugins/cinterion/mm-broadband-modem-mbim-cinterion.c b/src/plugins/cinterion/mm-broadband-modem-mbim-cinterion.c index b8d765d3..ec4cc91d 100644 --- a/src/plugins/cinterion/mm-broadband-modem-mbim-cinterion.c +++ b/src/plugins/cinterion/mm-broadband-modem-mbim-cinterion.c @@ -28,6 +28,7 @@ #include "mm-iface-modem-firmware.h" #include "mm-iface-modem-location.h" #include "mm-iface-modem-voice.h" +#include "mm-base-modem-at.h" #include "mm-broadband-modem-mbim-cinterion.h" #include "mm-shared-cinterion.h" @@ -52,6 +53,136 @@ G_DEFINE_TYPE_EXTENDED (MMBroadbandModemMbimCinterion, mm_broadband_modem_mbim_c G_IMPLEMENT_INTERFACE (MM_TYPE_IFACE_MODEM_TIME, iface_modem_time_init) G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_CINTERION, shared_cinterion_init)) +/*****************************************************************************/ +/* Load revision (Modem interface) */ + +static gchar * +load_revision_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (res), error); +} + +static void +at_load_revision_ready (MMBaseModem *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + const gchar *output = NULL; + g_autofree gchar *revision = NULL; + g_autoptr(GMatchInfo) match_info = NULL; + g_autoptr(GRegex) r = NULL; + + output = mm_base_modem_at_command_finish (self, res, &error); + if (error) { + mm_obj_dbg (self, "unable to get AT revision output"); + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + /* MV31 modem replies with: '^VERSION: xxx' */ + /* MV32 modem replies with: '+VERSION: xxx' */ + r = g_regex_new ("[+^]VERSION:\\s*(.*)", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + if (!g_regex_match_full (r, output, strlen(output), 0, 0, &match_info, &error)) { + if (error) + g_task_return_error (task, error); + else + g_task_return_new_error (task, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't match '^VERSION:' or '+VERSION:' in reply: '%s'", + output); + g_object_unref (task); + } + + if (!g_match_info_matches (match_info)) { + g_task_return_new_error ( + task, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Failed to parse 'AT^VERSION=1' or 'AT+VERSION=1' reply: '%s'", + output); + g_object_unref (task); + return; + } + + revision = mm_get_string_unquoted_from_match_info (match_info, 1); + if (revision == NULL) { + g_task_return_new_error ( + task, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Could not find revision in reply: '%s'", + output); + g_object_unref (task); + return; + } + + mm_obj_dbg (self, "got AT revision '%s'", revision); + g_task_return_pointer (task, g_strdup (revision), g_free); + g_object_unref (task); +} + +static void +mbim_load_revision_ready (MMIfaceModem *self, + GAsyncResult *res, + GTask *task) +{ + gchar *revision = NULL; + + revision = iface_modem_parent->load_revision_finish (self, res, NULL); + + mm_obj_dbg (self, "got mbim revision '%s'\n", revision); + g_task_return_pointer (task, revision, g_free); + g_object_unref (task); +} + +static void +load_revision (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + MMPortSerialAt *at_port = NULL; + guint product_id; + + task = g_task_new (self, NULL, callback, user_data); + at_port = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); + product_id = mm_base_modem_get_product_id (MM_BASE_MODEM(self)); + + if (at_port) { + /* MV31 */ + if (product_id == 0x00b3) { + mm_base_modem_at_command (MM_BASE_MODEM(self), + "AT^VERSION=1", + 3, + TRUE, + (GAsyncReadyCallback) at_load_revision_ready, + task); + return; + } + /* MV32 */ + if (product_id == 0x00f1) { + mm_base_modem_at_command (MM_BASE_MODEM(self), + "AT+VERSION=1", + 3, + TRUE, + (GAsyncReadyCallback) at_load_revision_ready, + task); + return; + } + } + + /* + * Only call parent function, if no AT port is available + * or the product ID does not match + */ + iface_modem_parent->load_revision (self, (GAsyncReadyCallback)mbim_load_revision_ready, task); +} + /*****************************************************************************/ MMBroadbandModemMbimCinterion * @@ -88,8 +219,10 @@ iface_modem_init (MMIfaceModemInterface *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); - iface->reset = mm_shared_cinterion_modem_reset; - iface->reset_finish = mm_shared_cinterion_modem_reset_finish; + iface->reset = mm_shared_cinterion_modem_reset; + iface->reset_finish = mm_shared_cinterion_modem_reset_finish; + iface->load_revision_finish = load_revision_finish; + iface->load_revision = load_revision; } static MMIfaceModemInterface * From 5d4b0d3516e5178a628bfe282778b14cc650e068 Mon Sep 17 00:00:00 2001 From: Florian Eckert Date: Fri, 30 May 2025 13:31:08 +0200 Subject: [PATCH 4/5] cinterion: add hardware revision read via AT for MV31 and MV32 Reading the hardware revision via the mbim interface returns an incorrect hardware revision string. The output is always set to 'h/w revision: Thales Snapdragon X55' Hardware | manufacturer: Cinterion ... | h/w revision: Thales Snapdragon X55 This is an incorrect value returned by the MBIM interface. The MV31 and MV32 does have a dedicated AT Command to get the correct hardware revision via 'AT+SKUID'. If the hardware revision is read via the AT command, the output looks as follows. Hardware | manufacturer: Cinterion ... | h/w revision: V065 Signed-off-by: Florian Eckert --- .../mm-broadband-modem-mbim-cinterion.c | 129 +++++++++++++++++- 1 file changed, 125 insertions(+), 4 deletions(-) diff --git a/src/plugins/cinterion/mm-broadband-modem-mbim-cinterion.c b/src/plugins/cinterion/mm-broadband-modem-mbim-cinterion.c index ec4cc91d..a23c55d5 100644 --- a/src/plugins/cinterion/mm-broadband-modem-mbim-cinterion.c +++ b/src/plugins/cinterion/mm-broadband-modem-mbim-cinterion.c @@ -183,6 +183,125 @@ load_revision (MMIfaceModem *self, iface_modem_parent->load_revision (self, (GAsyncReadyCallback)mbim_load_revision_ready, task); } +/*****************************************************************************/ +/* Load hardware revision (Modem interface) */ + +static gchar * +load_hardware_revision_finish (MMIfaceModem *self, + GAsyncResult *res, + GError **error) +{ + return g_task_propagate_pointer (G_TASK (res), error); +} + +static void +at_load_hardware_revision_ready (MMBaseModem *self, + GAsyncResult *res, + GTask *task) +{ + GError *error = NULL; + const gchar *output = NULL; + g_autofree gchar *revision = NULL; + g_autoptr(GMatchInfo) match_info = NULL; + g_autoptr(GRegex) r = NULL; + + output = mm_base_modem_at_command_finish (self, res, &error); + if (error) { + mm_obj_dbg (self, "unable to get AT hardware revision output"); + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + r = g_regex_new ("HW Revision:\\s*(.*)\\s*", G_REGEX_RAW | G_REGEX_OPTIMIZE, 0, NULL); + if (!g_regex_match_full (r, output, strlen(output), 0, 0, &match_info, &error)) { + if (error) + g_task_return_error (task, error); + else + g_task_return_new_error (task, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Couldn't match 'HW Revision:' in reply: '%s'", + output); + g_object_unref (task); + return; + } + + if (!g_match_info_matches (match_info)) { + g_task_return_new_error ( + task, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Failed to parse 'AT+SKUID' reply: '%s'", + output); + g_object_unref (task); + return; + } + + revision = mm_get_string_unquoted_from_match_info (match_info, 1); + if (revision == NULL) { + g_task_return_new_error ( + task, + MM_CORE_ERROR, + MM_CORE_ERROR_FAILED, + "Could not find hardware revision in reply: '%s'", + output); + g_object_unref (task); + return; + } + + mm_obj_dbg (self, "got AT hardware revision '%s'", revision); + g_task_return_pointer (task, g_strdup (revision), g_free); + g_object_unref (task); +} + +static void +mbim_load_hardware_revision_ready (MMIfaceModem *self, + GAsyncResult *res, + GTask *task) +{ + gchar *revision = NULL; + + revision = iface_modem_parent->load_revision_finish (self, res, NULL); + + mm_obj_dbg (self, "got mbim hardware revision '%s'", revision); + g_task_return_pointer (task, revision, g_free); + g_object_unref (task); +} + +static void +load_hardware_revision (MMIfaceModem *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + MMPortSerialAt *at_port = NULL; + guint product_id; + + task = g_task_new (self, NULL, callback, user_data); + at_port = mm_base_modem_peek_port_primary (MM_BASE_MODEM (self)); + product_id = mm_base_modem_get_product_id (MM_BASE_MODEM(self)); + + if (at_port) { + /* MV31 or MV32 */ + if ((product_id == 0x00f1) || (product_id == 0x00b3)) { + mm_base_modem_at_command (MM_BASE_MODEM(self), + "AT+SKUID", + 3, + TRUE, + (GAsyncReadyCallback) at_load_hardware_revision_ready, + task); + return; + } + } + + /* + * Only call parent function, if no AT port is available + * or the product ID does not match + */ + iface_modem_parent->load_revision (self, (GAsyncReadyCallback)mbim_load_hardware_revision_ready, task); +} + /*****************************************************************************/ MMBroadbandModemMbimCinterion * @@ -219,10 +338,12 @@ iface_modem_init (MMIfaceModemInterface *iface) { iface_modem_parent = g_type_interface_peek_parent (iface); - iface->reset = mm_shared_cinterion_modem_reset; - iface->reset_finish = mm_shared_cinterion_modem_reset_finish; - iface->load_revision_finish = load_revision_finish; - iface->load_revision = load_revision; + iface->reset = mm_shared_cinterion_modem_reset; + iface->reset_finish = mm_shared_cinterion_modem_reset_finish; + iface->load_revision_finish = load_revision_finish; + iface->load_revision = load_revision; + iface->load_hardware_revision_finish = load_hardware_revision_finish; + iface->load_hardware_revision = load_hardware_revision; } static MMIfaceModemInterface * From 1a9602ab6a8a182cdc4c7962f5f99490c94c5c9c Mon Sep 17 00:00:00 2001 From: Florian Eckert Date: Fri, 23 May 2025 14:32:28 +0200 Subject: [PATCH 5/5] cinterion: ignore carrier revision MV31 MBIM modem firmware revisions are not matched with carrier configs. Different VoLTE profiles might be selected by the user causing the carrier config revision to change. Since fwupd uses this string for firmware updates, it gets confused and thinks the user has an update available. Signed-off-by: Florian Eckert --- src/plugins/cinterion/mm-broadband-modem-mbim-cinterion.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plugins/cinterion/mm-broadband-modem-mbim-cinterion.c b/src/plugins/cinterion/mm-broadband-modem-mbim-cinterion.c index a23c55d5..8201304b 100644 --- a/src/plugins/cinterion/mm-broadband-modem-mbim-cinterion.c +++ b/src/plugins/cinterion/mm-broadband-modem-mbim-cinterion.c @@ -319,6 +319,8 @@ mm_broadband_modem_mbim_cinterion_new (const gchar *device, MM_BASE_MODEM_PLUGIN, plugin, MM_BASE_MODEM_VENDOR_ID, vendor_id, MM_BASE_MODEM_PRODUCT_ID, product_id, + /* include carrier information */ + MM_IFACE_MODEM_FIRMWARE_IGNORE_CARRIER, TRUE, /* MBIM bearer supports NET only */ MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE, MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,