Merge request !1367 from 'mr/20250530-cinterion'
cinterion: add MV32/MV31 udev rules and AT workarounds https://gitlab.freedesktop.org/mobile-broadband/ModemManager/-/merge_requests/1367
This commit is contained in:
@@ -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}=="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"
|
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"
|
LABEL="mm_cinterion_port_types_end"
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
#include "mm-iface-modem-firmware.h"
|
#include "mm-iface-modem-firmware.h"
|
||||||
#include "mm-iface-modem-location.h"
|
#include "mm-iface-modem-location.h"
|
||||||
#include "mm-iface-modem-voice.h"
|
#include "mm-iface-modem-voice.h"
|
||||||
|
#include "mm-base-modem-at.h"
|
||||||
#include "mm-broadband-modem-mbim-cinterion.h"
|
#include "mm-broadband-modem-mbim-cinterion.h"
|
||||||
#include "mm-shared-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_IFACE_MODEM_TIME, iface_modem_time_init)
|
||||||
G_IMPLEMENT_INTERFACE (MM_TYPE_SHARED_CINTERION, shared_cinterion_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 *
|
MMBroadbandModemMbimCinterion *
|
||||||
@@ -69,6 +319,8 @@ mm_broadband_modem_mbim_cinterion_new (const gchar *device,
|
|||||||
MM_BASE_MODEM_PLUGIN, plugin,
|
MM_BASE_MODEM_PLUGIN, plugin,
|
||||||
MM_BASE_MODEM_VENDOR_ID, vendor_id,
|
MM_BASE_MODEM_VENDOR_ID, vendor_id,
|
||||||
MM_BASE_MODEM_PRODUCT_ID, product_id,
|
MM_BASE_MODEM_PRODUCT_ID, product_id,
|
||||||
|
/* include carrier information */
|
||||||
|
MM_IFACE_MODEM_FIRMWARE_IGNORE_CARRIER, TRUE,
|
||||||
/* MBIM bearer supports NET only */
|
/* MBIM bearer supports NET only */
|
||||||
MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
|
MM_BASE_MODEM_DATA_NET_SUPPORTED, TRUE,
|
||||||
MM_BASE_MODEM_DATA_TTY_SUPPORTED, FALSE,
|
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_modem_parent = g_type_interface_peek_parent (iface);
|
||||||
|
|
||||||
iface->reset = mm_shared_cinterion_modem_reset;
|
iface->reset = mm_shared_cinterion_modem_reset;
|
||||||
iface->reset_finish = mm_shared_cinterion_modem_reset_finish;
|
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 *
|
static MMIfaceModemInterface *
|
||||||
|
|||||||
Reference in New Issue
Block a user