diff --git a/src/plugins/cinterion/77-mm-cinterion-port-types.rules b/src/plugins/cinterion/77-mm-cinterion-port-types.rules index 77f3b8ab..4ef89a1b 100644 --- a/src/plugins/cinterion/77-mm-cinterion-port-types.rules +++ b/src/plugins/cinterion/77-mm-cinterion-port-types.rules @@ -68,4 +68,22 @@ 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" + +# 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" diff --git a/src/plugins/cinterion/mm-broadband-modem-mbim-cinterion.c b/src/plugins/cinterion/mm-broadband-modem-mbim-cinterion.c index b8d765d3..8201304b 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,255 @@ 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); +} + +/*****************************************************************************/ +/* 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 * @@ -69,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, @@ -88,8 +340,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->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 *