fp6-img/aports/temp/modemmanager/0002-shared-qmi-unlock-AFW-gated-GNSS-engines-at-LOC-star.patch

255 lines
12 KiB
Diff
Raw Permalink Normal View History

From 2924dab906b3dc9e3b48e766637618ce53615c23 Mon Sep 17 00:00:00 2001
From: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
Date: Thu, 2 Jul 2026 15:30:48 +0200
Subject: [PATCH 2/3] shared-qmi: unlock AFW-gated GNSS engines at LOC start
Newer Snapdragon GNSS engines (e.g. gnss8 in the SM7635/Fairphone 6)
only allow positioning for clients registered as application framework
("AFW", i.e. the Android framework) clients: for anyone else,
QMI_LOC_REG_EVENTS fails with INVALID_ARGUMENT and QMI_LOC_START with
GENERAL_FAILURE ("IsPosRequestAllowed: FORCED_DISABLED" in the
engine's own logic). ModemManager issued START first and never
identified itself, so GPS could not be enabled at all on these devices.
Reorder the LOC bring-up to register-events -> start (the order the
Qualcomm location HAL uses), identifying as an AFW client with the
client identification TLVs added in libqmi 1.39.1, and bump the libqmi
requirement accordingly.
Isolating the TLVs on the gated engine showed the client type is the
only TLV actually checked (the client string and Enable Positioning
Request Notification are ignored for AFW clients, matching their
documentation in location_service_v02.h), so only the client type and
an informational "MM" client string are sent. Should an engine reject
the registration carrying the identification TLVs, it is retried once
without them, i.e. exactly what was always sent before.
This changes the LOC messages every QMI modem sees, and it has only
been tested on the Fairphone 6: the effect of the new TLVs and of the
reordering on other LOC engines is unknown (the retry above bounds the
worst case for the TLVs, not for the reordering). Regression testing
on other QMI modems would be very welcome.
The tracking-events registration that previously followed a successful
START is thereby folded into the initial register-events; the LOC
client and NMEA handler are still only recorded in the private info
once START has succeeded. This also fixes a pre-existing issue where a
register-events failure after a successful START returned an error
while leaving the engine running with no indication handlers connected.
Signed-off-by: Jorijn van der Graaf <jorijnvdgraaf@catcrafts.net>
---
meson.build | 2 +-
src/mm-shared-qmi.c | 139 +++++++++++++++++++++++++++-----------------
2 files changed, 86 insertions(+), 55 deletions(-)
diff --git a/meson.build b/meson.build
index 3cf29e80..e5e3d0be 100644
--- a/meson.build
+++ b/meson.build
@@ -277,7 +277,7 @@ config_h.set('WITH_MBIM', enable_mbim)
# QMI support (enabled by default)
enable_qmi = get_option('qmi')
if enable_qmi
- qmi_glib_dep = dependency('qmi-glib', version: '>= 1.37.95',
+ qmi_glib_dep = dependency('qmi-glib', version: '>= 1.39.1',
required: not get_option('subproject-fallback'))
if not qmi_glib_dep.found() and get_option('subproject-fallback')
diff --git a/src/mm-shared-qmi.c b/src/mm-shared-qmi.c
index d93698a6..09aa987a 100644
--- a/src/mm-shared-qmi.c
+++ b/src/mm-shared-qmi.c
@@ -5646,33 +5646,67 @@ pds_gps_service_state_start_ready (QmiClientPds *client,
qmi_message_pds_set_auto_tracking_state_input_unref (input);
}
+static void loc_register_events_ready (QmiClientLoc *client, GAsyncResult *res, GTask *task);
+
+/* Send the LOC event registration, optionally identifying as an
+ * application framework ("AFW", i.e. the Android framework) client: some
+ * GNSS engines (e.g. gnss8 in the SM7635) only allow positioning for AFW
+ * clients, and reject both event registration and QMI_LOC_START for
+ * unidentified clients. */
static void
-loc_register_events_ready (QmiClientLoc *client,
- GAsyncResult *res,
- GTask *task)
+gps_engine_loc_register_events (QmiClientLoc *client,
+ GTask *task,
+ gboolean identify)
{
- MMSharedQmi *self;
- Private *priv;
- QmiMessageLocRegisterEventsOutput *output;
- GError *error = NULL;
+ QmiMessageLocRegisterEventsInput *input;
+ guint64 event_mask;
+
+ event_mask = QMI_LOC_EVENT_REGISTRATION_FLAG_NMEA |
+ QMI_LOC_EVENT_REGISTRATION_FLAG_INJECT_TIME_REQUEST |
+ QMI_LOC_EVENT_REGISTRATION_FLAG_INJECT_POSITION_REQUEST;
+ input = qmi_message_loc_register_events_input_new ();
+ qmi_message_loc_register_events_input_set_event_registration_mask (input, event_mask, NULL);
+ if (identify) {
+ qmi_message_loc_register_events_input_set_client_type (input, QMI_LOC_CLIENT_TYPE_AFW, NULL);
+ qmi_message_loc_register_events_input_set_client_string_id (input, "MM", NULL);
+ }
+ g_task_set_task_data (task, GUINT_TO_POINTER (identify), NULL);
+ qmi_client_loc_register_events (client,
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback) loc_register_events_ready,
+ task);
+ qmi_message_loc_register_events_input_unref (input);
+}
+
+static void
+loc_start_ready (QmiClientLoc *client,
+ GAsyncResult *res,
+ GTask *task)
+{
+ MMSharedQmi *self;
+ Private *priv;
+ QmiMessageLocStartOutput *output;
+ GError *error = NULL;
- output = qmi_client_loc_register_events_finish (client, res, &error);
- if (!output) {
+ output = qmi_client_loc_start_finish (client, res, &error);
+ if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
g_task_return_error (task, error);
g_object_unref (task);
return;
- }
+ }
- if (!qmi_message_loc_register_events_output_get_result (output, &error)) {
- g_prefix_error (&error, "Couldn't not register tracking events: ");
+ if (!qmi_message_loc_start_output_get_result (output, &error)) {
+ g_prefix_error (&error, "Couldn't start GPS engine: ");
g_task_return_error (task, error);
g_object_unref (task);
- qmi_message_loc_register_events_output_unref (output);
+ qmi_message_loc_start_output_unref (output);
return;
}
- qmi_message_loc_register_events_output_unref (output);
+ qmi_message_loc_start_output_unref (output);
self = g_task_get_source_object (task);
priv = get_private (self);
@@ -5691,16 +5725,16 @@ loc_register_events_ready (QmiClientLoc *client,
}
static void
-loc_start_ready (QmiClientLoc *client,
- GAsyncResult *res,
- GTask *task)
+loc_register_events_ready (QmiClientLoc *client,
+ GAsyncResult *res,
+ GTask *task)
{
- QmiMessageLocRegisterEventsInput *input;
- QmiMessageLocStartOutput *output;
- guint64 event_mask;
- GError *error = NULL;
+ MMSharedQmi *self;
+ QmiMessageLocRegisterEventsOutput *output;
+ QmiMessageLocStartInput *input;
+ GError *error = NULL;
- output = qmi_client_loc_start_finish (client, res, &error);
+ output = qmi_client_loc_register_events_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
g_task_return_error (task, error);
@@ -5708,32 +5742,40 @@ loc_start_ready (QmiClientLoc *client,
return;
}
- if (!qmi_message_loc_start_output_get_result (output, &error)) {
- g_prefix_error (&error, "Couldn't start GPS engine: ");
+ if (!qmi_message_loc_register_events_output_get_result (output, &error)) {
+ qmi_message_loc_register_events_output_unref (output);
+ /* Engines predating the client identification TLVs may reject the
+ * identified registration; retry once without identifying, which
+ * matches what was always sent before */
+ if (g_task_get_task_data (task)) {
+ self = g_task_get_source_object (task);
+ mm_obj_dbg (self, "couldn't register location events as AFW client: %s; retrying without client identification", error->message);
+ g_clear_error (&error);
+ gps_engine_loc_register_events (client, task, FALSE);
+ return;
+ }
+ g_prefix_error (&error, "Couldn't register location events: ");
g_task_return_error (task, error);
g_object_unref (task);
- qmi_message_loc_start_output_unref (output);
return;
}
- qmi_message_loc_start_output_unref (output);
+ qmi_message_loc_register_events_output_unref (output);
- input = qmi_message_loc_register_events_input_new ();
- event_mask = QMI_LOC_EVENT_REGISTRATION_FLAG_NMEA |
- QMI_LOC_EVENT_REGISTRATION_FLAG_INJECT_TIME_REQUEST |
- QMI_LOC_EVENT_REGISTRATION_FLAG_INJECT_POSITION_REQUEST;
- qmi_message_loc_register_events_input_set_event_registration_mask (
- input, event_mask, NULL);
- qmi_client_loc_register_events (client,
- input,
- 10,
- NULL,
- (GAsyncReadyCallback) loc_register_events_ready,
- task);
- qmi_message_loc_register_events_input_unref (input);
+ input = qmi_message_loc_start_input_new ();
+ qmi_message_loc_start_input_set_session_id (input, DEFAULT_LOC_SESSION_ID, NULL);
+ qmi_message_loc_start_input_set_intermediate_report_state (input, QMI_LOC_INTERMEDIATE_REPORT_STATE_DISABLE, NULL);
+ qmi_message_loc_start_input_set_minimum_interval_between_position_reports (input, 1000, NULL);
+ qmi_message_loc_start_input_set_fix_recurrence_type (input, QMI_LOC_FIX_RECURRENCE_TYPE_REQUEST_PERIODIC_FIXES, NULL);
+ qmi_client_loc_start (client,
+ input,
+ 10,
+ NULL,
+ (GAsyncReadyCallback) loc_start_ready,
+ task);
+ qmi_message_loc_start_input_unref (input);
}
-
static void
start_gps_engine (MMSharedQmi *self,
GAsyncReadyCallback callback,
@@ -5771,20 +5813,9 @@ start_gps_engine (MMSharedQmi *self,
MM_PORT_QMI_FLAG_DEFAULT,
NULL);
if (client) {
- QmiMessageLocStartInput *input;
-
- input = qmi_message_loc_start_input_new ();
- qmi_message_loc_start_input_set_session_id (input, DEFAULT_LOC_SESSION_ID, NULL);
- qmi_message_loc_start_input_set_intermediate_report_state (input, QMI_LOC_INTERMEDIATE_REPORT_STATE_DISABLE, NULL);
- qmi_message_loc_start_input_set_minimum_interval_between_position_reports (input, 1000, NULL);
- qmi_message_loc_start_input_set_fix_recurrence_type (input, QMI_LOC_FIX_RECURRENCE_TYPE_REQUEST_PERIODIC_FIXES, NULL);
- qmi_client_loc_start (QMI_CLIENT_LOC (client),
- input,
- 10,
- NULL,
- (GAsyncReadyCallback) loc_start_ready,
- task);
- qmi_message_loc_start_input_unref (input);
+ /* Register events before starting; register-before-start is the
+ * order the Qualcomm location HAL uses */
+ gps_engine_loc_register_events (QMI_CLIENT_LOC (client), task, TRUE);
return;
}
--
2.55.0