---
drivers/ifxmodem/agps.c | 438 +++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 438 insertions(+), 0 deletions(-)
create mode 100644 drivers/ifxmodem/agps.c
diff --git a/drivers/ifxmodem/agps.c b/drivers/ifxmodem/agps.c
new file mode 100644
index 0000000..9b68b7e
--- /dev/null
+++ b/drivers/ifxmodem/agps.c
@@ -0,0 +1,438 @@
+/*
+ *
+ * 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 "gatchat.h"
+#include "gatresult.h"
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/agps.h>
+
+#include "util.h"
+#include "ifxmodem.h"
+
+struct agps_data {
+ GAtChat *chat;
+ unsigned int vendor;
+ enum ofono_access_technology rad_acc_tech;
+};
+
+struct ofono_agps;
+static enum ofono_access_technology rad_acc_tech;
+static const char *none_prefix[] = { NULL };
+
+#define FRAME_LEN 128
+
+static void pos_request_notify(GAtResult *result, gpointer user_data)
+{
+ struct ofono_agps *agps = user_data;
+ int framelen;
+ int frametype;
+ GAtResultIter iter;
+ struct ofono_lcs_frame lcsframe;
+ const char *messageframe;
+ unsigned char frame[FRAME_LEN];
+ long hexframelen;
+
+ /* Assuming Pos Req format: %XPOSR: <frametype>,<framelen>,<frame> */
+
+ g_at_result_iter_init(&iter, result);
+
+ if (!g_at_result_iter_next(&iter, "%%XPOSR:"))
+ return;
+
+ if (!g_at_result_iter_next_number(&iter, &frametype))
+ return;
+
+ if (!g_at_result_iter_next_number(&iter, &framelen))
+ return;
+
+ if (framelen > FRAME_LEN) {
+ ofono_error("Got POS request msg more than max buffer size!");
+ return;
+ }
+
+ messageframe = g_at_result_pdu(result);
+
+ if (strlen(messageframe) > sizeof(frame) * 2) { /*Hex, 2 chars/byte*/
+ ofono_error("Message frame too long!");
+ return;
+ }
+
+ if (decode_hex_own_buf(messageframe, -1, &hexframelen, 0,
+ frame) == NULL) {
+ ofono_error("Unable to hex-decode the AGPS frame");
+ return;
+ }
+
+ DBG("Got POS request data: %s, %ld", frame, hexframelen);
+
+ if (hexframelen != framelen) {
+ ofono_error("hexframelen not equal to reported framelen");
+ return;
+ }
+
+ lcsframe.lcs_frame_type = frametype;
+ lcsframe.frame_length = framelen;
+ lcsframe.raw_frame = (unsigned char *)frame;
+
+ ofono_agps_lcs_frame_notify(agps, lcsframe);
+}
+
+static void inj_time_notify(GAtResult *result, gpointer user_data)
+{
+
+ struct cb_data *cbd = user_data;
+ struct agps_data *agd = cbd->user;
+ ofono_agps_inject_time_cb_t cb = cbd->cb;
+ struct ofono_lcs_radio_fn rf;
+ struct ofono_error error = { .type = OFONO_ERROR_TYPE_NO_ERROR };
+ GAtResultIter iter;
+
+ g_at_result_iter_init(&iter, result);
+
+ if (!g_at_result_iter_next(&iter, "%%XFTI:"))
+ return;
+
+ if (RADIO_ACCESS_TECHNOLOGY_GSM == rad_acc_tech) {
+
+ int fn; /* range 0 - 2715647 (2048*26*51) */
+ int ts; /* range 0 - 7 */
+ int tsb; /* range 0 - 156 */
+ int ta; /* range 0 - 63 */
+ int ba; /* range 0 - 1023 */
+ int bc; /* range 0 - 64 */
+
+ /* %XFTI:<frameNum>,<TimeSlot>,<TimeSlotBit>,<TimeAdv>,
+ * <ChannelNum>,<ChannelId>
+ */
+ if (!g_at_result_iter_next_number(&iter, &fn))
+ goto err;
+
+ if (!g_at_result_iter_next_number(&iter, &ts))
+ goto err;
+
+ if (!g_at_result_iter_next_number(&iter, &tsb))
+ goto err;
+
+ if (!g_at_result_iter_next_number(&iter, &ta))
+ goto err;
+
+ if (!g_at_result_iter_next_number(&iter, &ba))
+ goto err;
+
+ if (!g_at_result_iter_next_number(&iter, &bc))
+ goto err;
+
+ DBG("GSM Inject Response: fn = %d ts = %d tsb = %d ta = %d"
+ "ba = %d bc = %d ", fn, ts, tsb, ta, ba, bc);
+
+ rf.gsm_frame_number.TDMA_frame_number = fn;
+ rf.gsm_frame_number.TDMA_timeslot = ts;
+ rf.gsm_frame_number.timeslot_bit = tsb;
+ rf.gsm_frame_number.timing_advance = ta;
+ rf.gsm_frame_number.bcch_arfcn = ba;
+ rf.gsm_frame_number.bsic = bc;
+ rf.radio_access_technology = RADIO_ACCESS_TECHNOLOGY_GSM;
+
+ } else if (RADIO_ACCESS_TECHNOLOGY_UMTS == rad_acc_tech) {
+
+ int sfn; /* range 0 - 4095 */
+ int rs; /* enum ofono_rrc_state */
+ int rt; /* range 0 - 32766 */
+
+ /* %XFTI:<frameNum>,<RadioState>,<TripTime> */
+ if (!g_at_result_iter_next_number(&iter, &sfn))
+ goto err;
+
+ if (!g_at_result_iter_next_number(&iter, &rs))
+ goto err;
+
+ if (!g_at_result_iter_next_number(&iter, &rt))
+ goto err;
+
+ DBG("UMTS Inject Response: sfn = %d rs = %d tt = %d",
+ sfn, rs, rt);
+
+ rf.utran_frame_number.sfn = sfn;
+ rf.utran_frame_number.rrc_state = rs;
+ rf.utran_frame_number.round_trip_time = rt;
+ rf.radio_access_technology = RADIO_ACCESS_TECHNOLOGY_UMTS;
+
+ } else
+ goto err;
+
+ cb(&error, &rf, cbd->data);
+ return;
+
+err:
+ CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
+}
+
+static int ifx_agps_probe(struct ofono_agps *agps,
+ unsigned int vendor, void *data)
+{
+ GAtChat *chat = data;
+ struct agps_data *agd = ofono_agps_get_data(agps);
+
+ agd = g_try_new0(struct agps_data, 1);
+ if (!agd)
+ return -ENOMEM;
+
+ agd->chat = g_at_chat_clone(chat);
+ agd->vendor = vendor;
+
+ ofono_agps_set_data(agps, agd);
+
+ g_at_chat_register(agd->chat, "%%XPOSR:", pos_request_notify, TRUE,
+ agps, NULL);
+
+ ofono_agps_register(agps);
+
+ return 0;
+}
+
+static void ifx_agps_remove(struct ofono_agps *agps)
+{
+ struct agps_data *agd = ofono_agps_get_data(agps);
+
+ ofono_agps_remove(agps);
+ ofono_agps_set_data(agps, NULL);
+ g_at_chat_unref(agd->chat);
+ g_free(agd);
+}
+
+
+static void ifx_agps_receive_lcs_frame_cb(gboolean ok, GAtResult *result,
+ gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_agps_receive_lcs_frame_cb_t cb = cbd->cb;
+ struct ofono_error error;
+
+ decode_at_error(&error, g_at_result_final_response(result));
+ cb(&error, cbd->data);
+}
+
+ /*
+ * The GPS manager can enable or disable AGPS manager to receive
+ * lcs_frames from the Mobile network. If disabled, all Assistance Data
+ * and Position Requests from Mobile Network are not signalled to ofono.
+ */
+static void ifx_agps_receive_lcs_frames(struct ofono_agps *agps,
+ int enabled, ofono_agps_receive_lcs_frame_cb_t cb,
+ void *user_data)
+{
+ struct agps_data *data = ofono_agps_get_data(agps);
+ struct cb_data *cbd = cb_data_new(cb, user_data);
+ char *commbuf;
+ unsigned int id;
+
+ if (!cbd)
+ goto error;
+
+ commbuf = g_strdup_printf("AT%%XPOS=\"%d\"", enabled);
+
+ id = g_at_chat_send(data->chat, commbuf, none_prefix,
+ ifx_agps_receive_lcs_frame_cb, cbd, g_free);
+
+ g_free(commbuf);
+
+ if (id > 0)
+ return;
+error:
+ if (cbd)
+ g_free(cbd);
+
+ CALLBACK_WITH_FAILURE(cb, user_data);
+}
+
+static void ifx_agps_send_lcs_frame_cb(gboolean ok, GAtResult *result,
+ gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ ofono_agps_send_lcs_frame_cb_t cb = cbd->cb;
+ struct ofono_error error;
+
+ decode_at_error(&error, g_at_result_final_response(result));
+ cb(&error, cbd->data);
+}
+
+#define BUF_LEN 128
+
+ /* Assistance Data and Position Requests from the Mobile Network are
+ * signalled via the ofono_agps_lcs_frame_notify function and the
+ * oFono core to an external GPS manager. This GPS manager can reply
+ * to Position Requests with one or more Position Responses which
+ * are then send back to the modem via the send_lcs_frame function.
+ */
+static void ifx_agps_send_lcs_frame(struct ofono_agps *agps,
+ struct ofono_lcs_frame *frame,
+ ofono_agps_send_lcs_frame_cb_t cb,
+ void *user_data)
+{
+ struct agps_data *data = ofono_agps_get_data(agps);
+ struct cb_data *cbd = cb_data_new(cb, user_data);
+ char buf[BUF_LEN * 2 + 1];
+ char *commbuf;
+ unsigned int id;
+ int buflen;
+
+ if (!cbd)
+ goto error;
+
+ if (!frame->frame_length) {
+ ofono_error("ifx_agps_send_lcs_frame: Frame length Invalid");
+ goto error;
+ }
+
+ if (frame->frame_length > BUF_LEN) {
+ ofono_error("ifx_agps_send_lcs_frame: Frame length too long!");
+ goto error;
+ }
+
+ encode_hex_own_buf(frame->raw_frame, frame->frame_length, 0, buf);
+ buflen = strlen(buf);
+ DBG("Encoded AGPS Frame = %s %d", buf, buflen);
+
+ commbuf = g_strdup_printf("AT%%XPOSR=%d,%d,\"%s\"",
+ frame->lcs_frame_type, buflen, buf);
+
+ id = g_at_chat_send(data->chat, commbuf, none_prefix,
+ ifx_agps_send_lcs_frame_cb, cbd, g_free);
+
+ g_free(commbuf);
+
+ if (id > 0)
+ return;
+error:
+ if (cbd)
+ g_free(cbd);
+
+ CALLBACK_WITH_FAILURE(cb, user_data);
+}
+
+static void ifx_agps_inject_time_cb(gboolean ok, GAtResult *result,
+ gpointer user_data)
+{
+ struct cb_data *cbd = user_data;
+ struct agps_data *data = cbd->user;
+ ofono_agps_inject_time_cb_t cb = cbd->cb;
+ struct ofono_error error;
+
+ decode_at_error(&error, g_at_result_final_response(result));
+
+ if (!ok)
+ goto err;
+
+ g_at_chat_register(agd->chat, "%%XFTI:",
+ inj_time_notify, FALSE,
+ data, g_free);
+ return;
+
+err:
+ cb(&error, NULL, cbd->data);
+ g_free(cbd);
+}
+
+ /* The GPS manager can ask the modem to generate a HW pulse (time
+ * stamp) with a defined length and the modem replies indicates when
+ * it generates the pulse. But as the modem has no precise idae of
+ * Universal Time, it indicates at which radio frame number itgc
+ * generated the pulse. The GPS manager which knows the link between
+ * Universal Time and the Radio Frame number knows very precisely at
+ * what time the pulse was generated and its duration.
+ *
+ * Timing accuracy is typically a few microseconds.
+ */
+static void ifx_agps_inject_time(struct ofono_agps *agps,
+ int radio_access_technology,/* enum access_technology */
+ int pulse_length,/* duration of pulse in radio slots */
+ ofono_agps_inject_time_cb_t cb, void *user_data)
+{
+ struct agps_data *data = ofono_agps_get_data(agps);
+ struct cb_data *cbd = cb_data_new(cb, user_data);
+ char *buf;
+ unsigned int id;
+
+ if (!cbd)
+ goto error;
+
+ cbd->user = data;
+
+ if (radio_access_technology == RADIO_ACCESS_TECHNOLOGY_GSM) {
+ data->rad_acc_tech = radio_access_technology;
+ buf = g_strdup_printf("AT%%XFTI=\"%s%d\"", "GSM",
+ pulse_length);
+
+ } else if (radio_access_technology == RADIO_ACCESS_TECHNOLOGY_UMTS) {
+ data->rad_acc_tech = radio_access_technology;
+ buf = g_strdup_printf("AT%%XFTI=\"%s%d\"", "UMTS",
+ pulse_length);
+ } else
+ goto error;
+
+ id = g_at_chat_send(data->chat, buf, none_prefix,
+ ifx_agps_inject_time_cb, cbd, g_free);
+
+ g_free(buf);
+
+ if (id > 0)
+ return;
+
+error:
+ if (cbd)
+ g_free(cbd);
+
+ CALLBACK_WITH_FAILURE(cb, NULL, user_data);
+}
+
+static struct ofono_agps_driver driver = {
+ .name = "ifxmodem",
+ .probe = ifx_agps_probe,
+ .remove = ifx_agps_remove,
+ .receive_lcs_frames = ifx_agps_receive_lcs_frames,
+ .send_lcs_frame = ifx_agps_send_lcs_frame,
+ .inject_time = ifx_agps_inject_time
+};
+
+void ifx_agps_init()
+{
+ ofono_agps_driver_register(&driver);
+}
+
+void ifx_agps_exit()
+{
+ ofono_agps_driver_unregister(&driver);
+}
+
--
1.7.0.4