From: Jun Wei <jun.wei(a)intel.com>
---
drivers/ifxmodem/cell-info.c | 398 ++++++++++++++++++++++++++++++++++++++++++
1 files changed, 398 insertions(+), 0 deletions(-)
create mode 100644 drivers/ifxmodem/cell-info.c
diff --git a/drivers/ifxmodem/cell-info.c b/drivers/ifxmodem/cell-info.c
new file mode 100644
index 0000000..471d248
--- /dev/null
+++ b/drivers/ifxmodem/cell-info.c
@@ -0,0 +1,398 @@
+/*
+ *
+ * oFono - Open Source Telephony
+ *
+ * Copyright (C) 2008-2010 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/cell-info.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "ifxmodem.h"
+
+#define OFONO_MAX_NEIGHBOUR_CELL_NUMBER 15
+
+static const char *none_prefix[] = { NULL };
+static const char *xcellinfo_prefix[] = { "+XCELLINFO:", NULL };
+
+struct cell_info_data {
+ GAtChat *chat;
+};
+
+static gboolean handle_gsm_servingcell_info(GAtResultIter *iter,
+ struct ofono_cell_info_results *sc)
+{
+ int mcc, mnc, lac, ci, rxlev, bsic, bcch_car, true_freq, t_advance;
+
+ DBG("");
+
+ if (!g_at_result_iter_next_number(iter, &mcc)) {
+ ofono_error("Fail to parse mcc");
+ return FALSE;
+ }
+
+ if (!g_at_result_iter_next_number(iter, &mnc)) {
+ ofono_error("Fail to parse mnc");
+ return FALSE;
+ }
+
+ if (!g_at_result_iter_next_number(iter, &lac)) {
+ ofono_error("Fail to parse lac");
+ return FALSE;
+ }
+
+ if (!g_at_result_iter_next_number(iter, &ci)) {
+ ofono_error("Fail to parse ci");
+ return FALSE;
+ }
+
+ if (!g_at_result_iter_next_number(iter, &rxlev)) {
+ ofono_error("Fail to parse rxlev");
+ return FALSE;
+ }
+
+ if (!g_at_result_iter_next_number(iter, &bsic)) {
+ ofono_error("Fail to parse bsic");
+ return FALSE;
+ }
+
+ if (!g_at_result_iter_next_number(iter, &bcch_car)) {
+ ofono_error("Fail to parse bcch_car");
+ return FALSE;
+ }
+
+ if (!g_at_result_iter_next_number(iter, &true_freq)) {
+ ofono_error("Fail to parse true_freq");
+ return FALSE;
+ }
+
+ if (!g_at_result_iter_next_number(iter, &t_advance)) {
+ ofono_error("Fail to parse t_advance");
+ return FALSE;
+ }
+
+ sc->geran.serving_cell.lac = lac;
+ sc->geran.serving_cell.ci = ci;
+ sc->geran.serving_cell.arfcn = bcch_car;
+ sc->geran.serving_cell.bsic = bsic;
+ sc->geran.serving_cell.rxlev = rxlev;
+ sc->geran.serving_cell.ta = t_advance;
+ sc->geran.serving_cell.true_freq = true_freq;
+
+ snprintf(sc->mcc, sizeof(sc->mcc), "%d", mcc);
+ snprintf(sc->mnc, sizeof(sc->mnc), "%d", mnc);
+
+ return TRUE;
+}
+
+static gboolean handle_gsm_neighbourcell_info(GAtResultIter *iter,
+ struct ofono_cell_info_results *nc, int index)
+{
+ int lac, ci, rxlev, bsic, bcch_car;
+
+ DBG("");
+
+ if (!g_at_result_iter_next_number(iter, &lac)) {
+ ofono_error("Fail to parse lac");
+ return FALSE;
+ }
+
+ if (!g_at_result_iter_next_number(iter, &ci)) {
+ ofono_error("Fail to parse ci");
+ return FALSE;
+ }
+
+ if (!g_at_result_iter_next_number(iter, &rxlev)) {
+ ofono_error("Fail to parse rxlev");
+ return FALSE;
+ }
+
+ if (!g_at_result_iter_next_number(iter, &bsic)) {
+ ofono_error("Fail to parse bsic");
+ return FALSE;
+ }
+
+ if (!g_at_result_iter_next_number(iter, &bcch_car)) {
+ ofono_error("Fail to parse bcch_car");
+ return FALSE;
+ }
+
+ nc->geran.nmr[index].lac = lac;
+ nc->geran.nmr[index].ci = ci;
+ nc->geran.nmr[index].arfcn = bcch_car;
+ nc->geran.nmr[index].bsic = bsic;
+ nc->geran.nmr[index].rxlev = rxlev;
+
+ return TRUE;
+}
+
+static gboolean handle_umts_fdd_servingcell_info(GAtResultIter *iter,
+ struct ofono_cell_info_results *sc)
+{
+ int mcc, mnc, lac, uci, dl_freq, ul_freq, scrambling_code;
+
+ DBG("");
+
+ if (!g_at_result_iter_next_number(iter, &mcc)) {
+ ofono_error("Fail to parse mcc");
+ return FALSE;
+ }
+
+ if (!g_at_result_iter_next_number(iter, &mnc)) {
+ ofono_error("Fail to parse mnc");
+ return FALSE;
+ }
+
+ if (!g_at_result_iter_next_number(iter, &lac)) {
+ ofono_error("Fail to parse lac");
+ return FALSE;
+ }
+
+ if (!g_at_result_iter_next_number(iter, &uci)) {
+ ofono_error("Fail to parse uci");
+ return FALSE;
+ }
+
+ if (!g_at_result_iter_next_number(iter, &scrambling_code)) {
+ ofono_error("Fail to parse scrambling code");
+ return FALSE;
+ }
+
+ if (!g_at_result_iter_next_number(iter, &dl_freq)) {
+ ofono_error("Fail to parse dl freq");
+ return FALSE;
+ }
+
+ if (!g_at_result_iter_next_number(iter, &ul_freq)) {
+ ofono_error("Fail to parse ul freq");
+ return FALSE;
+ }
+
+ sc->utra_fdd.serving_cell.lac = lac;
+ sc->utra_fdd.serving_cell.uc = uci;
+ sc->utra_fdd.serving_cell.scrambling_code = scrambling_code;
+ sc->utra_fdd.serving_cell.dl_frequency = dl_freq;
+ sc->utra_fdd.serving_cell.ul_frequency = ul_freq;
+
+ snprintf(sc->mcc, sizeof(sc->mcc), "%d", mcc);
+ snprintf(sc->mnc, sizeof(sc->mnc), "%d", mnc);
+
+ return TRUE;
+}
+
+static void xcellinfo_query_cb(gboolean ok, GAtResult *result,
+ gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_cell_info_query_cb_t cb = cbd->cb;
+ struct ofono_error error;
+ GAtResultIter iter;
+ struct ofono_cell_info_results cellinfo;
+ gboolean gsm_servingcell_found = FALSE;
+ gboolean umts_fdd_servingcell_found = FALSE;
+ int num = 0;
+
+ decode_at_error(&error, g_at_result_final_response(result));
+
+ if (!ok) {
+ cb(&error, cellinfo, cbd->data);
+ return;
+ }
+
+ g_at_result_iter_init(&iter, result);
+
+ while (g_at_result_iter_next(&iter, "+XCELLINFO:")) {
+ int type;
+
+ if (!g_at_result_iter_next_number(&iter, &type)) {
+ ofono_warn("Fail to parse cell type info");
+ continue;
+ }
+
+ switch (type) {
+ case 0:
+ /* GSM serving cell */
+ if (gsm_servingcell_found) {
+ ofono_warn("More than 1 GSM serving cell found!");
+ continue;
+ }
+
+ gsm_servingcell_found = TRUE;
+ cellinfo.rat = OFONO_CELL_INFO_GERAN;
+
+ if (!handle_gsm_servingcell_info(&iter, &cellinfo)) {
+ ofono_warn("Wrong GSM serving cell info");
+ continue;
+ }
+
+ break;
+ case 1:
+ /* GSM neighbour cell */
+ num++;
+
+ if (num > OFONO_MAX_NEIGHBOUR_CELL_NUMBER) {
+ ofono_error("Wrong number of GSM neighbour cell");
+ continue;
+ }
+
+ cellinfo.rat = OFONO_CELL_INFO_GERAN;
+
+ if (!handle_gsm_neighbourcell_info(&iter, &cellinfo, (num - 1))) {
+ ofono_warn("Wrong GSM neighbour cell info");
+ continue;
+ }
+
+ cellinfo.geran.no_cells = num;
+
+ break;
+ case 2:
+ /* UMTS FDD serving cell */
+ if (umts_fdd_servingcell_found) {
+ ofono_warn("More than 1 UMTS FDD serving cell found!");
+ continue;
+ }
+
+ umts_fdd_servingcell_found = TRUE;
+ cellinfo.rat = OFONO_CELL_INFO_UTRA_FDD;
+
+ if (!handle_umts_fdd_servingcell_info(&iter, &cellinfo)) {
+ ofono_warn("Wrong UMTS FDD serving cell info");
+ continue;
+ }
+
+ break;
+ case 3:
+ /* UMTS FDD neighbour cell */
+ cellinfo.rat = OFONO_CELL_INFO_UTRA_FDD;
+
+ /*
+ * TODO: wait for the clarification from modem vendor
+ * on the neighbour cell info of UMTS FDD network
+ * handle_umts_fdd_neighbourcell_info(&iter, &cellinfo);
+ */
+
+ ofono_warn("UMTS FDD NBR cell is not supported");
+ continue;
+
+ default:
+ /* Other types are not supported */
+ ofono_error("Cell type %d is not supported", type);
+ CALLBACK_WITH_FAILURE(cb, cellinfo, cbd->data);
+ return;
+ }
+
+ }
+
+ cb(&error, cellinfo, cbd->data);
+}
+
+static void ifx_cell_info_query(struct ofono_cell_info *ci,
+ ofono_cell_info_query_cb_t cb,
+ void *data)
+{
+ struct cell_info_data *cid = ofono_cell_info_get_data(ci);
+ struct cb_data *cbd = cb_data_new(cb, data);
+ struct ofono_cell_info_results cellinfo;
+
+ DBG("");
+
+ if (!cbd)
+ goto error;
+
+ if (g_at_chat_send(cid->chat, "AT+XCELLINFO?", xcellinfo_prefix,
+ xcellinfo_query_cb, cbd, g_free) > 0)
+ return;
+
+error:
+ g_free(cbd);
+ CALLBACK_WITH_FAILURE(cb, cellinfo, data);
+
+}
+
+static void xcellinfo_support_cb(gboolean ok, GAtResult *result, gpointer user_data)
+{
+ struct ofono_cell_info *ci = user_data;
+
+ if (!ok)
+ return;
+
+ ofono_cell_info_register(ci);
+}
+
+static int ifx_cell_info_probe(struct ofono_cell_info *ci,
+ unsigned int vendor, void *data)
+{
+ GAtChat *chat = data;
+ struct cell_info_data *cid;
+
+ cid = g_try_new0(struct cell_info_data, 1);
+
+ if (!cid)
+ return -ENOMEM;
+
+ cid->chat = g_at_chat_clone(chat);
+
+ ofono_cell_info_set_data(ci, cid);
+
+ g_at_chat_send(cid->chat, "AT+XCELLINFO=?", none_prefix,
+ xcellinfo_support_cb, ci, NULL);
+
+ return 0;
+}
+
+static void ifx_cell_info_remove(struct ofono_cell_info *ci)
+{
+ struct cell_info_data *cid = ofono_cell_info_get_data(ci);
+
+ ofono_cell_info_set_data(ci, NULL);
+
+ g_at_chat_unref(cid->chat);
+ g_free(cid);
+}
+
+static struct ofono_cell_info_driver driver = {
+ .name = "ifxmodem",
+ .probe = ifx_cell_info_probe,
+ .remove = ifx_cell_info_remove,
+ .query = ifx_cell_info_query,
+};
+
+void ifx_cell_info_init()
+{
+ ofono_cell_info_driver_register(&driver);
+}
+
+void ifx_cell_info_exit()
+{
+ ofono_cell_info_driver_unregister(&driver);
+}
--
1.7.2.3