shared-qmi: handle inject time requests from the modem

To be able to have quick position fix GNSS engine needs to know
 - predicted orbits data,
 - current time,
 - approximate position.
Modem sends indications with requests, when it needs corresponding
information. Predicted orbits can be injected using
'mmcli -m any --location-inject-assistance-data=<file>'. But to
accelerate time to first fix after cold start extra information is
needed like time and coarse position. This commit injects UTC time.

UTC time inject requests are received, when GPS session is initiated
using QMI_LOC_START.

Tested on Quectel EG25-G.

Signed-off-by: Andrey Skvortsov <andrej.skvortzov@gmail.com>
This commit is contained in:
Andrey Skvortsov
2026-01-10 02:04:10 +03:00
parent 90e71e2817
commit 81101b0147
+193 -6
View File
@@ -91,6 +91,8 @@ typedef struct {
guint32 loc_assistance_data_max_file_size;
guint32 loc_assistance_data_max_part_size;
gulong loc_assistance_inject_time_req_indication_id;
/* Carrier config helpers */
gboolean config_active_default;
GArray *config_list;
@@ -119,6 +121,8 @@ private_free (Private *priv)
g_object_unref (priv->pds_client);
if (priv->loc_location_nmea_indication_id)
g_signal_handler_disconnect (priv->loc_client, priv->loc_location_nmea_indication_id);
if (priv->loc_assistance_inject_time_req_indication_id)
g_signal_handler_disconnect (priv->loc_client, priv->loc_assistance_inject_time_req_indication_id);
if (priv->loc_client)
g_object_unref (priv->loc_client);
if (priv->uim_slot_status_indication_id)
@@ -5670,6 +5674,7 @@ loc_start_ready (QmiClientLoc *client,
{
QmiMessageLocRegisterEventsInput *input;
QmiMessageLocStartOutput *output;
guint64 event_mask;
GError *error = NULL;
output = qmi_client_loc_start_finish (client, res, &error);
@@ -5691,8 +5696,10 @@ loc_start_ready (QmiClientLoc *client,
qmi_message_loc_start_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_message_loc_register_events_input_set_event_registration_mask (
input, QMI_LOC_EVENT_REGISTRATION_FLAG_NMEA, NULL);
input, event_mask, NULL);
qmi_client_loc_register_events (client,
input,
10,
@@ -6714,6 +6721,172 @@ loc_location_get_predicted_orbits_data_source_indication_timed_out (GTask *task)
return G_SOURCE_REMOVE;
}
/*****************************************************************************/
/* Location: Inject UTC time */
typedef struct {
guint timeout_id;
gulong indication_id;
QmiClientLoc *client;
} InjectTimeContext;
static void
inject_time_context_cleanup_action (InjectTimeContext *ctx)
{
if (ctx->timeout_id) {
g_source_remove (ctx->timeout_id);
ctx->timeout_id = 0;
}
if (ctx->indication_id) {
if (ctx->client)
g_signal_handler_disconnect (ctx->client, ctx->indication_id);
ctx->indication_id = 0;
}
}
static void
inject_time_context_free (InjectTimeContext *ctx)
{
inject_time_context_cleanup_action (ctx);
g_clear_object (&ctx->client);
g_slice_free (InjectTimeContext, ctx);
}
static void
inject_time_finish_task (GTask *_task,
GError *error)
{
InjectTimeContext *ctx;
g_autoptr(GTask) task = _task;
ctx = g_task_get_task_data (task);
inject_time_context_cleanup_action (ctx);
if (error)
g_task_return_error (task, error);
else
g_task_return_boolean (task, TRUE);
}
static gboolean
loc_location_inject_time_indication_timed_out (GTask *task)
{
inject_time_finish_task (task, g_error_new (MM_CORE_ERROR, MM_CORE_ERROR_ABORTED,
"Operation timed out"));
return G_SOURCE_REMOVE;
}
static void
loc_location_inject_time_indication_cb (QmiClientLoc *client,
QmiIndicationLocInjectUtcTimeOutput *output,
GTask *task)
{
QmiLocIndicationStatus status;
GError *error = NULL;
if (!qmi_indication_loc_inject_utc_time_output_get_indication_status (output, &status, &error))
g_prefix_error (&error, "QMI operation failed: ");
else
mm_error_from_qmi_loc_indication_status (status, &error);
inject_time_finish_task (task, error);
}
static void
loc_location_inject_time_ready (QmiClientLoc *client,
GAsyncResult *res,
GTask *task)
{
g_autoptr(QmiMessageLocInjectUtcTimeOutput) output = NULL;
GError *error = NULL;
output = qmi_client_loc_inject_utc_time_finish (client, res, &error);
if (!output) {
g_prefix_error (&error, "QMI operation failed: ");
inject_time_finish_task (task, error);
return;
}
if (!qmi_message_loc_inject_utc_time_output_get_result (output, &error)) {
inject_time_finish_task (task, error);
return;
}
}
static void
loc_location_inject_time_req_indication_cb (QmiClientLoc *client,
QmiIndicationLocInjectTimeRequestOutput *output,
MMSharedQmi *self)
{
g_autoptr(QmiMessageLocInjectUtcTimeInput) input = NULL;
guint64 utc_ms = 0;
GTask *task;
InjectTimeContext *ctx;
task = g_task_new (self, NULL, NULL, NULL);
ctx = g_slice_new0 (InjectTimeContext);
g_task_set_task_data (task, ctx, (GDestroyNotify)inject_time_context_free);
ctx->client = g_object_ref (client);
input = qmi_message_loc_inject_utc_time_input_new ();
utc_ms = g_get_real_time () / 1000;
qmi_message_loc_inject_utc_time_input_set_utc_time (input, utc_ms, NULL);
qmi_message_loc_inject_utc_time_input_set_time_uncertainty (input, 1000, NULL);
qmi_message_loc_inject_utc_time_input_set_time_source (input, QMI_LOC_INJECTED_TIME_SOURCE_UNKNOWN, NULL);
ctx->timeout_id = g_timeout_add_seconds (10,
(GSourceFunc) loc_location_inject_time_indication_timed_out,
task);
ctx->indication_id = g_signal_connect (client,
"inject-utc-time",
G_CALLBACK (loc_location_inject_time_indication_cb),
task);
qmi_client_loc_inject_utc_time (
QMI_CLIENT_LOC (ctx->client),
input,
10,
NULL, /* cancellable */
(GAsyncReadyCallback)loc_location_inject_time_ready,
task);
}
static void
loc_register_event_inject_req_ready (QmiClientLoc *client,
GAsyncResult *res,
GTask *task)
{
g_autoptr(QmiMessageLocRegisterEventsOutput) output = NULL;
g_autoptr(GError) error = NULL;
MMSharedQmi *self;
Private *priv;
output = qmi_client_loc_register_events_finish (client, res, &error);
if (!output) {
mm_obj_warn (client, "QMI operation failed: %s", error->message);
goto out;
}
if (!qmi_message_loc_register_events_output_get_result (output, &error)) {
mm_obj_warn (client, "Couldn't not register tracking events: %s", error->message);
goto out;
}
self = g_task_get_source_object (task);
priv = get_private (self);
g_assert (!priv->loc_assistance_inject_time_req_indication_id);
priv->loc_assistance_inject_time_req_indication_id =
g_signal_connect (client,
"inject-time-request",
G_CALLBACK (loc_location_inject_time_req_indication_cb),
self);
out:
g_task_return_int (task, MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_XTRA);
g_object_unref (task);
}
static void
loc_location_get_predicted_orbits_data_source_indication_cb (QmiClientLoc *client,
QmiIndicationLocGetPredictedOrbitsDataSourceOutput *output,
@@ -6772,13 +6945,27 @@ loc_location_get_predicted_orbits_data_source_indication_cb (QmiClientLoc
}
out:
if (error)
if (error) {
g_task_return_error (task, error);
else if (!supported)
g_object_unref (task);
} else if (!supported) {
g_task_return_int (task, MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_NONE);
else
g_task_return_int (task, MM_MODEM_LOCATION_ASSISTANCE_DATA_TYPE_XTRA);
g_object_unref (task);
g_object_unref (task);
} else {
g_autoptr(QmiMessageLocRegisterEventsInput) input = NULL;
guint64 event_mask;
event_mask = QMI_LOC_EVENT_REGISTRATION_FLAG_INJECT_TIME_REQUEST;
input = qmi_message_loc_register_events_input_new ();
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_event_inject_req_ready,
task);
}
}
static void