From: Wei, Jun Sent: Saturday, January 08, 2011 8:40 PM To: [email protected] Cc: Wei, Jun Subject: [PATCH v2 1/4] ifxmodem: Add modem driver to support Neighbor cell info
From: Jun Wei <[email protected]> --- drivers/ifxmodem/cell-info.c | 506 ++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 506 insertions(+), 0 deletions(-) create mode 100755 drivers/ifxmodem/cell-info.c diff --git a/drivers/ifxmodem/cell-info.c b/drivers/ifxmodem/cell-info.c new file mode 100755 index 0000000..5eeab8f --- /dev/null +++ b/drivers/ifxmodem/cell-info.c @@ -0,0 +1,506 @@ +/* + * + * 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" + +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 index) +{ + int mcc, mnc, lac, ci, rxlev, bsic, bcch_car, true_freq, t_advance; + + DBG(""); + + if (index >= OFONO_MAX_NMR_COUNT) { + ofono_error("Wrong number of GSM cells"); + return FALSE; + } + + 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->mnc = mnc; + sc->mcc = mcc; + + sc->geran.lac = lac; + sc->geran.ci = ci; + sc->geran.ta = t_advance; + sc->geran.nmr[index].arfcn = bcch_car; + sc->geran.nmr[index].bsic = bsic; + sc->geran.nmr[index].rxlev = rxlev; + + + 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 (index >= OFONO_MAX_NMR_COUNT) { + ofono_error("Wrong number of GSM neighbour cells"); + return FALSE; + } + + + if (!g_at_result_iter_next_number(iter, &lac)) { + ofono_error("Fail to parse lac"); + /* So far LAC is reported but not used */ + } + + if (!g_at_result_iter_next_number(iter, &ci)) { + ofono_error("Fail to parse ci"); + /* So far CI is reported but not used */ + } + + 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].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"); + /* So far LAC is reported but not used */ + } + + 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->mcc = mcc; + sc->mnc = mnc; + + sc->utran.ucid = uci; + sc->utran.dl_freq = dl_freq; + sc->utran.ul_freq = ul_freq; + sc->utran.sc = scrambling_code; + + return TRUE; +} + +static void fill_utran_mrl(struct measured_results_list *mrl, int dl_freq, + int rssi, int scrambling_code, int ecn0, int rscp, int pathloss) +{ + mrl->dl_freq = dl_freq; + mrl->rssi = rssi; + mrl->no_cells++; + mrl->cmr[0].sc = scrambling_code; + mrl->cmr[0].ecn0 = ecn0; + mrl->cmr[0].rscp = rscp; + mrl->cmr[0].pathloss = pathloss; +} + +static void fill_utran_cmr(struct cell_measured_results *cmr, + int scrambling_code, int ecn0, int rscp, int pathloss) +{ + cmr->sc = scrambling_code; + cmr->ecn0 = ecn0; + cmr->rscp = rscp; + cmr->pathloss = pathloss; +} + +static gboolean handle_umts_fdd_neighbourcell_info(GAtResultIter *iter, + struct ofono_cell_info_results *nc) +{ + int scrambling_code, dl_freq, rssi, rscp, ecn0, pathloss; + gboolean same_freq_found = FALSE; + int i, j; + + DBG(""); + + if (!g_at_result_iter_next_number(iter, &scrambling_code)) { + ofono_error("Fail to parse scramble 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, &rssi)) { + ofono_error("Fail to parse rssi"); + return FALSE; + } + + if (!g_at_result_iter_next_number(iter, &rscp)) { + ofono_error("Fail to parse rscp"); + return FALSE; + } + + if (!g_at_result_iter_next_number(iter, &ecn0)) { + ofono_error("Fail to parse ecn0"); + return FALSE; + } + + if (!g_at_result_iter_next_number(iter, &pathloss)) { + ofono_error("Fail to parse path loss"); + return FALSE; + } + + if (nc->utran.no_freq == 0) { + fill_utran_mrl(&(nc->utran.mrl[nc->utran.no_freq++]), + dl_freq, rssi, scrambling_code, ecn0, rscp, pathloss); + return TRUE; + } + + for (i = 0; i < nc->utran.no_freq && i < OFONO_MAX_MEAS_RES_LIST_COUNT; i++) { + if (nc->utran.mrl[i].dl_freq == dl_freq) { + same_freq_found = TRUE; + + for (j = 0; j < nc->utran.mrl[i].no_cells && j < OFONO_MAX_MEASURED_CELL_COUNT; j++) { + if (nc->utran.mrl[i].cmr[j].sc == scrambling_code) { + /* same frequency + same scramble code -> same cell */ + ofono_warn("same cell info has been reported"); + return TRUE; + } + } + + /* same frequency + different scramble code -> NEW cell */ + fill_utran_cmr(&(nc->utran.mrl[i].cmr[j]), + scrambling_code, ecn0, rscp, pathloss); + nc->utran.mrl[i].no_cells++; + DBG("no_cell: %d", nc->utran.mrl[i].no_cells); + } else { + continue; + } + } + + /* different frequency -> NEW cell */ + if (!same_freq_found) { + DBG("no_freq: %d", nc->utran.no_freq); + + if (nc->utran.no_freq >= OFONO_MAX_MEAS_RES_LIST_COUNT) + return FALSE; + + fill_utran_mrl(&(nc->utran.mrl[nc->utran.no_freq++]), + dl_freq, rssi, scrambling_code, ecn0, rscp, pathloss); + } + + 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_sc_found = FALSE; + gboolean umts_fdd_sc_found = FALSE; + int num = 0; + + DBG(""); + + decode_at_error(&error, g_at_result_final_response(result)); + + memset(&cellinfo, 0, sizeof(cellinfo)); + + if (!ok) { + cb(&error, NULL, 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 (umts_fdd_sc_found || gsm_sc_found) + continue; + + gsm_sc_found = TRUE; + cellinfo.rat = OFONO_CELL_TYPE_GERAN; + + if (!handle_gsm_servingcell_info(&iter, &cellinfo, num)) { + ofono_warn("Wrong GSM serving cell info"); + continue; + } + + cellinfo.geran.no_cells = ++num; + + break; + case 1: + /* GSM neighbour cell */ + if (umts_fdd_sc_found) + continue; + + cellinfo.rat = OFONO_CELL_TYPE_GERAN; + + if (!handle_gsm_neighbourcell_info(&iter, &cellinfo, num)) { + ofono_warn("Wrong GSM neighbour cell info"); + continue; + } + + cellinfo.geran.no_cells = ++num; + + break; + case 2: + if (gsm_sc_found || umts_fdd_sc_found) + continue; + + umts_fdd_sc_found = TRUE; + cellinfo.rat = OFONO_CELL_TYPE_UTRAN_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 */ + if (gsm_sc_found) + continue; + + cellinfo.rat = OFONO_CELL_TYPE_UTRAN_FDD; + + if (!handle_umts_fdd_neighbourcell_info(&iter, &cellinfo)) { + ofono_warn("Wrong UMTS FDD neighbour cell info"); + continue; + } + + break; + 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, ci); + + 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, NULL, 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; + + DBG(""); + + 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 _______________________________________________ ofono mailing list [email protected] http://lists.ofono.org/listinfo/ofono
