qmi: implement GetCellInfo for LTE, NR

This commit is contained in:
Oliver Kästner
2023-01-04 20:42:06 +01:00
committed by Aleksander Morgado
parent 6748fb7c2c
commit 652a562aeb
2 changed files with 251 additions and 1 deletions

View File

@@ -257,7 +257,7 @@ config_h.set('WITH_MBIM', enable_mbim)
# QMI support (enabled by default) # QMI support (enabled by default)
enable_qmi = get_option('qmi') enable_qmi = get_option('qmi')
if enable_qmi if enable_qmi
qmi_glib_dep = dependency('qmi-glib', version: '>= 1.33.2') qmi_glib_dep = dependency('qmi-glib', version: '>= 1.33.4')
endif endif
config_h.set('WITH_QMI', enable_qmi) config_h.set('WITH_QMI', enable_qmi)

View File

@@ -1721,6 +1721,254 @@ load_signal_quality (MMIfaceModem *self,
task); task);
} }
/*****************************************************************************/
/* Cell info */
static GList *
get_cell_info_finish (MMIfaceModem *self,
GAsyncResult *res,
GError **error)
{
return g_task_propagate_pointer (G_TASK (res), error);
}
static void
cell_info_list_free (GList *cell_info_list)
{
g_list_free_full (cell_info_list, g_object_unref);
}
/* Stolen from qmicli-nas.c as mm_bcd_to_string() doesn't correctly handle
* special filler byte (0xF) for 2-digit MNCs.
* ref: Table 10.5.3/3GPP TS 24.008 */
static gchar *
str_from_bcd_plmn (GArray *bcd)
{
static const gchar bcd_chars[] = "0123456789*#abc\0\0";
gchar *str;
guint i;
guint j;
if (!bcd || !bcd->len)
return NULL;
str = g_malloc (1 + (bcd->len * 2));
for (i = 0, j = 0 ; i < bcd->len; i++) {
str[j] = bcd_chars[g_array_index (bcd, guint8, i) & 0xF];
if (str[j])
j++;
str[j] = bcd_chars[(g_array_index (bcd, guint8, i) >> 4) & 0xF];
if (str[j])
j++;
}
str[j] = '\0';
return str;
}
static void
get_cell_info_ready (QmiClientNas *client,
GAsyncResult *res,
GTask *task)
{
QmiMessageNasGetCellLocationInfoOutput *output;
GError *error = NULL;
GList *list = NULL;
/* common vars */
GArray *operator;
guint32 cell_id;
guint16 arfcn;
GArray* cell_array;
GArray* frequency_array;
guint16 lte_tac;
guint16 lte_scell_id;
guint32 lte_timing_advance;
GArray *nr5g_tac;
guint64 nr5g_global_ci;
guint16 nr5g_pci;
gint16 nr5g_rsrq;
gint16 nr5g_rsrp;
gint16 nr5g_snr;
guint32 nr5g_arfcn;
output = qmi_client_nas_get_cell_location_info_finish (client, res, &error);
if (!output) {
g_task_return_error (task, error);
g_object_unref (task);
return;
}
if (!qmi_message_nas_get_cell_location_info_output_get_result (output, &error)) {
g_task_return_error (task, error);
g_object_unref (task);
qmi_message_nas_get_cell_location_info_output_unref (output);
return;
}
if (qmi_message_nas_get_cell_location_info_output_get_intrafrequency_lte_info_v2 (
output,
NULL /* ue in idle */,
&operator,
&lte_tac,
&cell_id,
&arfcn,
&lte_scell_id,
NULL /* cell reselect prio */,
NULL /* non-intra search thres */,
NULL /* scell low thres */,
NULL /* s intra search thres */,
&cell_array,
&error)) {
g_autofree gchar *operator_id = NULL;
g_autofree gchar *tac = NULL;
g_autofree gchar *ci = NULL;
guint i;
operator_id = str_from_bcd_plmn (operator);
/* Encoded in upper-case hexadecimal format without leading zeros, as specified in 3GPP TS 27.007. */
tac = g_strdup_printf ("%X", lte_tac);
ci = g_strdup_printf ("%X", cell_id);
for (i = 0; i < cell_array->len; i++) {
QmiMessageNasGetCellLocationInfoOutputIntrafrequencyLteInfoV2CellElement *element;
MMCellInfoLte *lte_info;
g_autofree gchar *pci = NULL;
element = &g_array_index (cell_array, QmiMessageNasGetCellLocationInfoOutputIntrafrequencyLteInfoV2CellElement, i);
lte_info = MM_CELL_INFO_LTE (mm_cell_info_lte_new_from_dictionary (NULL));
/* valid for all cells */
mm_cell_info_lte_set_operator_id (lte_info, operator_id);
mm_cell_info_lte_set_tac (lte_info, tac);
mm_cell_info_lte_set_earfcn (lte_info, arfcn);
/* this cell */
pci = g_strdup_printf ("%X", element->physical_cell_id);
mm_cell_info_lte_set_physical_ci (lte_info, pci);
mm_cell_info_lte_set_rsrp (lte_info, (0.1) * ((gdouble)element->rsrp));
mm_cell_info_lte_set_rsrq (lte_info, (0.1) * ((gdouble)element->rsrq));
/* only for serving cell, we get details about CGI and TA */
if (element->physical_cell_id == lte_scell_id) {
mm_cell_info_set_serving (MM_CELL_INFO (lte_info), TRUE);
mm_cell_info_lte_set_ci (lte_info, ci);
if (qmi_message_nas_get_cell_location_info_output_get_lte_info_timing_advance (output,
&lte_timing_advance,
NULL)) {
mm_cell_info_lte_set_timing_advance (lte_info, lte_timing_advance);
}
}
list = g_list_append (list, g_steal_pointer (&lte_info));
}
}
if (qmi_message_nas_get_cell_location_info_output_get_interfrequency_lte_info (output,
NULL /* UE in idle */,
&frequency_array,
NULL)) {
guint i;
for (i = 0; i < frequency_array->len; i++) {
QmiMessageNasGetCellLocationInfoOutputInterfrequencyLteInfoFrequencyElement *frequency;
MMCellInfoLte *lte_info;
guint j;
frequency = &g_array_index (frequency_array, QmiMessageNasGetCellLocationInfoOutputInterfrequencyLteInfoFrequencyElement, i);
arfcn = frequency->eutra_absolute_rf_channel_number;
cell_array = frequency->cell;
for (j = 0; j < cell_array->len; j++) {
QmiMessageNasGetCellLocationInfoOutputInterfrequencyLteInfoFrequencyElementCellElement *cell;
g_autofree gchar *pci = NULL;
cell = &g_array_index (cell_array, QmiMessageNasGetCellLocationInfoOutputInterfrequencyLteInfoFrequencyElementCellElement, j);
lte_info = MM_CELL_INFO_LTE (mm_cell_info_lte_new_from_dictionary (NULL));
pci = g_strdup_printf ("%X", cell->physical_cell_id);
mm_cell_info_lte_set_earfcn (lte_info, arfcn);
mm_cell_info_lte_set_physical_ci (lte_info, pci);
mm_cell_info_lte_set_rsrp (lte_info, (0.1) * ((gdouble)cell->rsrp));
mm_cell_info_lte_set_rsrq (lte_info, (0.1) * ((gdouble)cell->rsrq));
list = g_list_append (list, g_steal_pointer (&lte_info));
}
}
}
if (qmi_message_nas_get_cell_location_info_output_get_nr5g_cell_information (output,
&operator,
&nr5g_tac,
&nr5g_global_ci,
&nr5g_pci,
&nr5g_rsrq,
&nr5g_rsrp,
&nr5g_snr,
&error)) {
MMCellInfoNr5g *nr5g_info;
g_autofree gchar *operator_id = NULL;
g_autofree gchar *tac = NULL;
g_autofree gchar *global_ci = NULL;
g_autofree gchar *pci = NULL;
operator_id = str_from_bcd_plmn (operator);
g_assert (nr5g_tac->len == 3);
/* Encoded in upper-case hexadecimal format without leading zeros, as specified in 3GPP TS 27.007. */
tac = g_strdup_printf ("%X", ((((g_array_index (nr5g_tac, guint8, 0) << 8) |
g_array_index (nr5g_tac, guint8, 1)) << 8) |
g_array_index (nr5g_tac, guint8, 2)));
global_ci = g_strdup_printf ("%" G_GINT64_MODIFIER "X", nr5g_global_ci);
pci = g_strdup_printf ("%X", nr5g_pci);
nr5g_info = MM_CELL_INFO_NR5G (mm_cell_info_nr5g_new_from_dictionary (NULL));
mm_cell_info_set_serving (MM_CELL_INFO (nr5g_info), TRUE);
mm_cell_info_nr5g_set_operator_id (nr5g_info, operator_id);
mm_cell_info_nr5g_set_tac (nr5g_info, tac);
mm_cell_info_nr5g_set_ci (nr5g_info, global_ci);
mm_cell_info_nr5g_set_physical_ci (nr5g_info, pci);
mm_cell_info_nr5g_set_rsrq (nr5g_info, (0.1) * ((gdouble)nr5g_rsrq));
mm_cell_info_nr5g_set_rsrp (nr5g_info, (0.1) * ((gdouble)nr5g_rsrp));
mm_cell_info_nr5g_set_sinr (nr5g_info, (0.1) * ((gdouble)nr5g_snr));
if (qmi_message_nas_get_cell_location_info_output_get_nr5g_arfcn (output, &nr5g_arfcn, &error)) {
mm_cell_info_nr5g_set_nrarfcn (nr5g_info, nr5g_arfcn);
}
list = g_list_append (list, g_steal_pointer (&nr5g_info));
}
g_task_return_pointer (task, list, (GDestroyNotify)cell_info_list_free);
g_object_unref (task);
qmi_message_nas_get_cell_location_info_output_unref (output);
}
static void
get_cell_info (MMIfaceModem *self,
GAsyncReadyCallback callback,
gpointer user_data)
{
QmiClient *client = NULL;
GTask *task;
if (!mm_shared_qmi_ensure_client (MM_SHARED_QMI (self), QMI_SERVICE_NAS, &client, callback, user_data))
return;
task = g_task_new (self, NULL, callback, user_data);
mm_obj_dbg (self, "getting cell info...");
qmi_client_nas_get_cell_location_info (QMI_CLIENT_NAS (client),
NULL,
10,
NULL,
(GAsyncReadyCallback)get_cell_info_ready,
task);
}
/*****************************************************************************/ /*****************************************************************************/
/* Powering up/down/off the modem (Modem interface) */ /* Powering up/down/off the modem (Modem interface) */
@@ -13241,6 +13489,8 @@ iface_modem_init (MMIfaceModem *iface)
iface->set_current_modes_finish = mm_shared_qmi_set_current_modes_finish; iface->set_current_modes_finish = mm_shared_qmi_set_current_modes_finish;
iface->load_signal_quality = load_signal_quality; iface->load_signal_quality = load_signal_quality;
iface->load_signal_quality_finish = load_signal_quality_finish; iface->load_signal_quality_finish = load_signal_quality_finish;
iface->get_cell_info = get_cell_info;
iface->get_cell_info_finish = get_cell_info_finish;
iface->load_current_bands = mm_shared_qmi_load_current_bands; iface->load_current_bands = mm_shared_qmi_load_current_bands;
iface->load_current_bands_finish = mm_shared_qmi_load_current_bands_finish; iface->load_current_bands_finish = mm_shared_qmi_load_current_bands_finish;
iface->set_current_bands = mm_shared_qmi_set_current_bands; iface->set_current_bands = mm_shared_qmi_set_current_bands;