diff options
author | Oliver Kästner <git@oliver-kaestner.de> | 2023-01-04 20:42:06 +0100 |
---|---|---|
committer | Aleksander Morgado <aleksandermj@chromium.org> | 2023-02-03 10:23:44 +0000 |
commit | 652a562aebcd22e16962ec57c0176acdbf41876a (patch) | |
tree | 67408b054a07eefe67b3694511df3aecd207b4b1 | |
parent | 6748fb7c2c3e1935c581eb0a0f50e2f30fd6235e (diff) |
qmi: implement GetCellInfo for LTE, NR
-rw-r--r-- | meson.build | 2 | ||||
-rw-r--r-- | src/mm-broadband-modem-qmi.c | 250 |
2 files changed, 251 insertions, 1 deletions
diff --git a/meson.build b/meson.build index 328cac19..9af10570 100644 --- a/meson.build +++ b/meson.build @@ -257,7 +257,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.33.2') + qmi_glib_dep = dependency('qmi-glib', version: '>= 1.33.4') endif config_h.set('WITH_QMI', enable_qmi) diff --git a/src/mm-broadband-modem-qmi.c b/src/mm-broadband-modem-qmi.c index 9627d601..e267b9f0 100644 --- a/src/mm-broadband-modem-qmi.c +++ b/src/mm-broadband-modem-qmi.c @@ -1722,6 +1722,254 @@ load_signal_quality (MMIfaceModem *self, } /*****************************************************************************/ +/* 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, + <e_tac, + &cell_id, + &arfcn, + <e_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, + <e_timing_advance, + NULL)) { + mm_cell_info_lte_set_timing_advance (lte_info, lte_timing_advance); + } + } + + list = g_list_append (list, g_steal_pointer (<e_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 (<e_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) */ typedef struct { @@ -13241,6 +13489,8 @@ iface_modem_init (MMIfaceModem *iface) iface->set_current_modes_finish = mm_shared_qmi_set_current_modes_finish; iface->load_signal_quality = load_signal_quality; 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_finish = mm_shared_qmi_load_current_bands_finish; iface->set_current_bands = mm_shared_qmi_set_current_bands; |