+/*
+ * location-module
+ *
+ * Copyright (c) 2010-2013 Samsung Electronics Co., Ltd. All rights reserved.
+ *
+ * Contact: Youngae Kang <youngae.kang@samsung.com>, Minjune Kim <sena06.kim@samsung.com>
+ * Genie Kim <daejins.kim@samsung.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glib.h>
+
+#include <location-module.h>
+
+#include <geoclue/geoclue-position.h>
+#include <geoclue/geoclue-velocity.h>
+#include <geoclue/geoclue-provider.h>
+#include <dlfcn.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include "log.h"
+
+typedef struct{
+ char devname[256];
+ GeocluePosition *pos;
+ GeoclueVelocity *vel;
+ LocModStatusCB status_cb;
+ LocModPositionCB pos_cb;
+ LocModVelocityCB vel_cb;
+ gpointer userdata;
+ gboolean is_started;
+} GeoclueGpsdData;
+
+static void
+status_callback (GeoclueProvider *provider,
+ gint status,
+ gpointer userdata)
+{
+ GeoclueGpsdData* gpsd = (GeoclueGpsdData*)userdata;
+ g_return_if_fail(gpsd->status_cb);
+
+ switch(status){
+ case GEOCLUE_STATUS_ERROR:
+ case GEOCLUE_STATUS_UNAVAILABLE:
+ case GEOCLUE_STATUS_ACQUIRING:
+ MOD_LOGD("Status callback>> GEOCLUE_STATUS_ACQUIRING/ERROR/UNAVAILABLE");
+ gpsd->status_cb(FALSE, LOCATION_STATUS_NO_FIX, gpsd->userdata);
+ break;
+ case GEOCLUE_STATUS_AVAILABLE:
+ MOD_LOGD("Status callback>> GEOCLUE_STATUS_AVAILABLE");
+ gpsd->status_cb(TRUE, LOCATION_STATUS_3D_FIX, gpsd->userdata);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+position_callback (GeocluePosition *position,
+ GeocluePositionFields fields,
+ int timestamp,
+ double latitude,
+ double longitude,
+ double altitude,
+ GeoclueAccuracy *accuracy,
+ gpointer userdata)
+{
+ GeoclueGpsdData* gpsd = (GeoclueGpsdData*)userdata;
+ g_return_if_fail(gpsd->pos_cb);
+
+ GeoclueAccuracyLevel level;
+ double horiz_acc;
+ double vert_acc;
+ geoclue_accuracy_get_details (accuracy, &level, &horiz_acc, &vert_acc);
+
+ LocationPosition *pos = NULL;
+ LocationAccuracy *acc = NULL;
+
+ if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE &&
+ fields & GEOCLUE_POSITION_FIELDS_LONGITUDE) {
+ if(fields & GEOCLUE_POSITION_FIELDS_ALTITUDE)
+ pos = location_position_new (timestamp, latitude, longitude, altitude, LOCATION_STATUS_3D_FIX);
+ else pos = location_position_new (timestamp, latitude, longitude, 0, LOCATION_STATUS_2D_FIX);
+ } else pos = location_position_new (0, 0, 0, 0, LOCATION_STATUS_NO_FIX);
+
+ if (accuracy) {
+ GeoclueAccuracyLevel level;
+ double horiz_acc;
+ double vert_acc;
+ geoclue_accuracy_get_details (accuracy, &level, &horiz_acc, &vert_acc);
+ acc = location_accuracy_new (LOCATION_ACCURACY_LEVEL_DETAILED, horiz_acc, vert_acc);
+ } else acc = location_accuracy_new (LOCATION_ACCURACY_LEVEL_NONE, horiz_acc, vert_acc);
+
+ MOD_LOGD("Position callback>> time(%d) lat(%f) long(%f) alt(%f) status(%d) acc_level(%d) hoz_acc(%f) vert_acc(%f)",
+ pos->timestamp, pos->latitude, pos->longitude, pos->altitude, pos->status,
+ acc->level, acc->horizontal_accuracy, acc->vertical_accuracy);
+ gpsd->pos_cb(TRUE, pos, acc, gpsd->userdata);
+ location_position_free (pos);
+ location_accuracy_free (acc);
+}
+
+static void
+velocity_callback (GeoclueVelocity *velocity,
+ GeoclueVelocityFields fields,
+ int timestamp,
+ double speed,
+ double direction,
+ double climb,
+ gpointer userdata)
+{
+ GeoclueGpsdData* gpsd = (GeoclueGpsdData*)userdata;
+ g_return_if_fail(gpsd->vel_cb);
+
+ LocationVelocity *vel = NULL;
+ LocationAccuracy *acc = NULL;
+
+ if (fields & GEOCLUE_VELOCITY_FIELDS_SPEED &&
+ fields & GEOCLUE_VELOCITY_FIELDS_DIRECTION) {
+ if (fields & GEOCLUE_VELOCITY_FIELDS_CLIMB) vel = location_velocity_new (timestamp, speed, direction, climb);
+ else vel = location_velocity_new (timestamp, speed, direction, 0);
+ acc = location_accuracy_new (LOCATION_ACCURACY_LEVEL_DETAILED, 0, 0);
+ } else {
+ vel = location_velocity_new (0, 0, 0, 0);
+ acc = location_accuracy_new (LOCATION_ACCURACY_LEVEL_NONE, 0, 0);
+ }
+ MOD_LOGD("Velocity callback>> timestamp(%d) speed(%f) direction(%f) climb(%f) acc_level(%d) hoz_acc(%f) vert_acc(%f)",
+ vel->timestamp, vel->speed, vel->direction, vel->climb,
+ acc->level, acc->horizontal_accuracy, acc->vertical_accuracy);
+
+ gpsd->vel_cb(TRUE, vel, acc, gpsd->userdata);
+ location_velocity_free (vel);
+ location_accuracy_free (acc);
+}
+
+static void
+_unref_geoclue(GeoclueGpsdData* gpsd)
+{
+ if(gpsd->pos) {
+ g_signal_handlers_disconnect_by_func(G_OBJECT (GEOCLUE_PROVIDER(gpsd->pos)), G_CALLBACK (status_callback), gpsd);
+ g_signal_handlers_disconnect_by_func(G_OBJECT (GEOCLUE_PROVIDER(gpsd->pos)), G_CALLBACK (position_callback), gpsd);
+ g_object_unref (gpsd->pos);
+ gpsd->pos = NULL;
+ }
+
+ if( gpsd->vel != NULL){
+ g_signal_handlers_disconnect_by_func(G_OBJECT (GEOCLUE_PROVIDER(gpsd->vel)), G_CALLBACK (velocity_callback), gpsd);
+ g_object_unref (gpsd->vel);
+ gpsd->vel = NULL;
+ }
+ gpsd->is_started = FALSE;
+}
+
+static gboolean
+_ref_geoclue(GeoclueGpsdData* gpsd)
+{
+ if(gpsd->is_started == TRUE){
+ MOD_LOGW ("geoclue-gpsd is alredy started");
+ return TRUE;
+ }
+
+ gchar *service, *path;
+ service = g_strdup_printf ("org.freedesktop.Geoclue.Providers.Gpsd");
+ path = g_strdup_printf ("/org/freedesktop/Geoclue/Providers/Gpsd");
+
+ if(!gpsd->pos){
+ gpsd->pos = geoclue_position_new (service, path);
+ }
+ if(!gpsd->vel){
+ gpsd->vel = geoclue_velocity_new (service, path);
+ }
+ g_free (service);
+ g_free (path);
+ if(!gpsd->pos || !gpsd->vel){
+ MOD_LOGW ("Error while creating Geoclue object.");
+ _unref_geoclue(gpsd);
+ return FALSE;
+ }
+
+ gpsd->is_started = TRUE;
+ g_signal_connect (G_OBJECT (GEOCLUE_PROVIDER(gpsd->pos)), "status-changed", G_CALLBACK (status_callback), gpsd);
+ g_signal_connect (G_OBJECT (GEOCLUE_PROVIDER(gpsd->pos)), "position-changed", G_CALLBACK (position_callback), gpsd);
+ g_signal_connect (G_OBJECT (GEOCLUE_PROVIDER(gpsd->vel)), "velocity-changed", G_CALLBACK (velocity_callback), gpsd);
+
+ return TRUE;
+}
+
+static int
+start(gpointer handle, LocModStatusCB status_cb, LocModPositionCB pos_cb, LocModVelocityCB vel_cb, gpointer userdata)
+{
+ MOD_LOGD("start");
+ GeoclueGpsdData* gpsd = (GeoclueGpsdData*)handle;
+ g_return_val_if_fail(gpsd, LOCATION_ERROR_NOT_AVAILABLE);
+ g_return_val_if_fail(status_cb, LOCATION_ERROR_NOT_AVAILABLE);
+ g_return_val_if_fail(pos_cb, LOCATION_ERROR_NOT_AVAILABLE);
+ g_return_val_if_fail(vel_cb, LOCATION_ERROR_NOT_AVAILABLE);
+
+ gpsd->status_cb = status_cb;
+ gpsd->pos_cb = pos_cb;
+ gpsd->vel_cb = vel_cb;
+ gpsd->userdata = userdata;
+ if (!_ref_geoclue(gpsd)) return LOCATION_ERROR_NOT_AVAILABLE;
+ return LOCATION_ERROR_NONE;
+}
+
+static int
+stop(gpointer handle)
+{
+ MOD_LOGD("stop");
+ GeoclueGpsdData* gpsd = (GeoclueGpsdData*)handle;
+ g_return_val_if_fail(gpsd, LOCATION_ERROR_NOT_AVAILABLE);
+ g_return_val_if_fail(gpsd->status_cb, LOCATION_ERROR_NOT_AVAILABLE);
+ _unref_geoclue(gpsd);
+ gpsd->status_cb(FALSE, LOCATION_STATUS_NO_FIX, gpsd->userdata);
+ return LOCATION_ERROR_NONE;
+}
+
+static int
+get_position(
+ gpointer handle,
+ LocationPosition **position,
+ LocationAccuracy **accuracy)
+{
+ GeoclueGpsdData* gpsd = (GeoclueGpsdData*)handle;
+ g_return_val_if_fail(gpsd, LOCATION_ERROR_NOT_AVAILABLE);
+ g_return_val_if_fail(gpsd->pos, LOCATION_ERROR_NOT_AVAILABLE);
+ g_return_val_if_fail(position, LOCATION_ERROR_PARAMETER);
+ g_return_val_if_fail(accuracy, LOCATION_ERROR_PARAMETER);
+
+ GeocluePositionFields fields;
+ int timestamp;
+ double lat, lon, alt;
+ GeoclueAccuracy *_accuracy = NULL;
+ GError *error = NULL;
+
+ fields = geoclue_position_get_position (gpsd->pos, ×tamp,
+ &lat, &lon, &alt,
+ &_accuracy, &error);
+ if (error) {
+ MOD_LOGD ("Error getting position: %s", error->message);
+ g_error_free (error);
+ return LOCATION_ERROR_NOT_AVAILABLE;
+ }
+
+ if (fields & GEOCLUE_POSITION_FIELDS_LATITUDE &&
+ fields & GEOCLUE_POSITION_FIELDS_LONGITUDE) {
+ if(fields & GEOCLUE_POSITION_FIELDS_ALTITUDE) *position = location_position_new (timestamp, lat, lon, alt, LOCATION_STATUS_3D_FIX);
+ else *position = location_position_new (timestamp, lat, lon, 0, LOCATION_STATUS_2D_FIX);
+ } else *position = location_position_new (0, 0, 0, 0, LOCATION_STATUS_NO_FIX);
+
+ if (_accuracy) {
+ GeoclueAccuracyLevel level;
+ double horiz_acc;
+ double vert_acc;
+ geoclue_accuracy_get_details (_accuracy, &level, &horiz_acc, &vert_acc);
+ *accuracy = location_accuracy_new (LOCATION_ACCURACY_LEVEL_DETAILED, horiz_acc, vert_acc);
+ geoclue_accuracy_free (_accuracy);
+ } else *accuracy = location_accuracy_new (LOCATION_ACCURACY_LEVEL_NONE, 0, 0);
+
+ return LOCATION_ERROR_NONE;
+}
+
+static int
+get_velocity(
+ gpointer handle,
+ LocationVelocity **velocity,
+ LocationAccuracy **accuracy)
+{
+ GeoclueGpsdData* gpsd = (GeoclueGpsdData*)handle;
+ g_return_val_if_fail(gpsd, LOCATION_ERROR_NOT_AVAILABLE);
+ g_return_val_if_fail(gpsd->vel, LOCATION_ERROR_NOT_AVAILABLE);
+ g_return_val_if_fail(velocity, LOCATION_ERROR_PARAMETER);
+ g_return_val_if_fail(accuracy, LOCATION_ERROR_PARAMETER);
+
+ GeoclueVelocityFields fields;
+ int timestamp;
+ double spd, dir, climb;
+ GError *error = NULL;
+
+ fields = geoclue_velocity_get_velocity (gpsd->vel, ×tamp,
+ &spd, &dir, &climb,
+ &error);
+ if (error) {
+ MOD_LOGD ("Error getting velocity: %s", error->message);
+ g_error_free (error);
+ return LOCATION_ERROR_NOT_AVAILABLE;
+ }
+
+ if (fields & GEOCLUE_VELOCITY_FIELDS_SPEED &&
+ fields & GEOCLUE_VELOCITY_FIELDS_DIRECTION) {
+ if (fields & GEOCLUE_VELOCITY_FIELDS_CLIMB) *velocity = location_velocity_new (timestamp, spd, dir, climb);
+ else *velocity = location_velocity_new (timestamp, spd, dir, 0);
+ *accuracy = location_accuracy_new (LOCATION_ACCURACY_LEVEL_DETAILED, 0, 0);
+ } else {
+ *velocity = location_velocity_new (0, 0, 0, 0);
+ *accuracy = location_accuracy_new (LOCATION_ACCURACY_LEVEL_NONE, 0, 0);
+ }
+ return LOCATION_ERROR_NONE;
+}
+
+static int
+get_nmea(gpointer handle,
+ char **nmea_data)
+{
+ MOD_LOGD("get_nmea: not available");
+ return LOCATION_ERROR_NOT_AVAILABLE;
+}
+
+static int
+get_satellite(gpointer handle,
+ LocationSatellite **satellite)
+{
+ MOD_LOGD("get_satellite: not available");
+ return LOCATION_ERROR_NOT_AVAILABLE;
+}
+
+static int
+set_devname(gpointer handle,
+ const char *devname)
+{
+ GeoclueGpsdData* gpsd = (GeoclueGpsdData*)handle;
+ g_return_val_if_fail(gpsd, LOCATION_ERROR_NOT_AVAILABLE);
+ g_return_val_if_fail(gpsd->devname, LOCATION_ERROR_NOT_AVAILABLE);
+ g_return_val_if_fail(devname, LOCATION_ERROR_PARAMETER);
+ MOD_LOGD("set_devname: %s --> %s", gpsd->devname, devname);
+ g_stpcpy(gpsd->devname, devname);
+
+ return LOCATION_ERROR_NONE;
+}
+
+
+static int
+get_devname(gpointer handle,
+ char **devname)
+{
+ GeoclueGpsdData* gpsd = (GeoclueGpsdData*)handle;
+ g_return_val_if_fail(gpsd, LOCATION_ERROR_NOT_AVAILABLE);
+ g_return_val_if_fail(gpsd->devname, LOCATION_ERROR_NOT_AVAILABLE);
+ g_return_val_if_fail(devname, LOCATION_ERROR_PARAMETER);
+
+ *devname = g_strdup(gpsd->devname);
+ MOD_LOGD("get_devname: %s", *devname);
+
+ return LOCATION_ERROR_NONE;
+}
+
+LOCATION_MODULE_API gpointer
+init(LocModGpsOps* ops)
+{
+ MOD_LOGD("init");
+ g_return_val_if_fail(ops, NULL);
+ ops->start = start;
+ ops->stop = stop ;
+ ops->get_position = get_position;
+ ops->get_velocity = get_velocity;
+ Dl_info info;
+ if (dladdr(&get_position, &info) == 0) {
+ MOD_LOGD("failed to get module name");
+ } else if (g_str_has_prefix(info.dli_fname, "gps")) {
+ ops->get_nmea = get_nmea;
+ ops->get_satellite = get_satellite;
+ ops->set_devname = set_devname;
+ ops->get_devname = get_devname;
+ }
+
+ GeoclueGpsdData* gpsd = g_new0(GeoclueGpsdData, 1);
+ g_return_val_if_fail(gpsd, NULL);
+
+ g_stpcpy(gpsd->devname, "/dev/rfcomm0");
+ gpsd->pos = NULL;
+ gpsd->vel = NULL;
+ gpsd->status_cb= NULL;
+ gpsd->pos_cb = NULL;
+ gpsd->vel_cb = NULL;
+ gpsd->userdata = NULL;
+ gpsd->is_started = FALSE;
+
+ return (gpointer)gpsd;
+}
+
+LOCATION_MODULE_API void
+shutdown(gpointer handle)
+{
+ MOD_LOGD("shutdown");
+ GeoclueGpsdData* gpsd = (GeoclueGpsdData*)handle;
+ g_return_if_fail(gpsd);
+ _unref_geoclue(gpsd);
+ if(gpsd->status_cb) gpsd->status_cb(FALSE, LOCATION_STATUS_NO_FIX, gpsd->userdata);
+ g_free(gpsd);
+}