---
 drivers/gemaltomodem/location-reporting.c | 317 ++++++++++++++++++++--
 1 file changed, 291 insertions(+), 26 deletions(-)

diff --git a/drivers/gemaltomodem/location-reporting.c 
b/drivers/gemaltomodem/location-reporting.c
index bcfe00e5..375b7bfa 100644
--- a/drivers/gemaltomodem/location-reporting.c
+++ b/drivers/gemaltomodem/location-reporting.c
@@ -3,6 +3,7 @@
  *  oFono - Open Source Telephony
  *
  *  Copyright (C) 2017 Vincent Cesson. All rights reserved.
+ *  Copyright (C) 2018 Gemalto M2M
  *
  *  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
@@ -31,38 +32,62 @@
 #include <unistd.h>
 
 #include <glib.h>
+#include <gdbus.h>
 
 #include <ofono/log.h>
 #include <ofono/modem.h>
 #include <ofono/location-reporting.h>
 
+#include <src/storage.h>
+
 #include "gatchat.h"
 #include "gatresult.h"
 #include "gattty.h"
 
+#include "ofono.h"
+#include "common.h"
+
 #include "gemaltomodem.h"
 
+struct ofono_location_reporting {
+       DBusMessage *pending;
+       const struct ofono_location_reporting_driver *driver;
+       void *driver_data;
+       struct ofono_atom *atom;
+       ofono_bool_t enabled;
+       char *client_owner;
+       guint disconnect_watch;
+};
+
 static const char *sgpsc_prefix[] = { "^SGPSC:", NULL };
 
+#define MAX_GNSS_PROPERTIES            (64)
+#define MAX_GNSS_STRLEN                        (32)
+
+typedef struct _gnss_property {
+       uint type; // dynamic or stored, exclude, value_s or value_i
+       char name[MAX_GNSS_STRLEN];
+       union {
+               char value_s[32];
+               int value_i;
+       };
+} gnss_property;
+
 struct gps_data {
        GAtChat *chat;
+       gnss_property properties[MAX_GNSS_PROPERTIES];
 };
 
 static void gemalto_gps_disable_cb(gboolean ok, GAtResult *result,
                                                        gpointer user_data)
 {
        struct cb_data *cbd = user_data;
-       struct ofono_location_reporting *lr = cbd->user;
        ofono_location_reporting_disable_cb_t cb = cbd->cb;
 
-       DBG("lr=%p, ok=%d", lr, ok);
-
        if (!ok) {
                struct ofono_error error;
-
                decode_at_error(&error, g_at_result_final_response(result));
                cb(&error, cbd->data);
-
                return;
        }
 
@@ -77,8 +102,6 @@ static void gemalto_location_reporting_disable(
        struct gps_data *gd = ofono_location_reporting_get_data(lr);
        struct cb_data *cbd = cb_data_new(cb, data);
 
-       DBG("lr=%p", lr);
-
        cbd->user = lr;
 
        if (g_at_chat_send(gd->chat, "AT^SGPSC=\"Engine\",0", sgpsc_prefix,
@@ -86,36 +109,32 @@ static void gemalto_location_reporting_disable(
                return;
 
        CALLBACK_WITH_FAILURE(cb, data);
-
        g_free(cbd);
 }
 
 static int enable_data_stream(struct ofono_location_reporting *lr)
 {
        struct ofono_modem *modem;
-       const char *gps_dev;
+       const char *gnss_dev;
        GHashTable *options;
        GIOChannel *channel;
        int fd;
 
        modem = ofono_location_reporting_get_modem(lr);
-       gps_dev = ofono_modem_get_string(modem, "GPS");
-
+       gnss_dev = ofono_modem_get_string(modem, "GNSS");
        options = g_hash_table_new(g_str_hash, g_str_equal);
-       if (options == NULL)
+
+       if (!gnss_dev || !*gnss_dev || !options)
                return -1;
 
        g_hash_table_insert(options, "Baud", "115200");
-
-       channel = g_at_tty_open(gps_dev, options);
-
+       channel = g_at_tty_open(gnss_dev, options);
        g_hash_table_destroy(options);
 
-       if (channel == NULL)
+       if (!channel)
                return -1;
 
        fd = g_io_channel_unix_get_fd(channel);
-
        g_io_channel_set_close_on_unref(channel, FALSE);
        g_io_channel_unref(channel);
 
@@ -131,8 +150,6 @@ static void gemalto_sgpsc_cb(gboolean ok, GAtResult *result,
        struct ofono_error error;
        int fd;
 
-       DBG("lr=%p ok=%d", lr, ok);
-
        decode_at_error(&error, g_at_result_final_response(result));
 
        if (!ok) {
@@ -153,15 +170,14 @@ static void gemalto_sgpsc_cb(gboolean ok, GAtResult 
*result,
        close(fd);
 }
 
-static void gemalto_location_reporting_enable(struct ofono_location_reporting 
*lr,
+static void gemalto_location_reporting_enable(
+                                       struct ofono_location_reporting *lr,
                                        ofono_location_reporting_enable_cb_t cb,
                                        void *data)
 {
        struct gps_data *gd = ofono_location_reporting_get_data(lr);
        struct cb_data *cbd = cb_data_new(cb, data);
 
-       DBG("lr=%p", lr);
-
        cbd->user = lr;
 
        if (g_at_chat_send(gd->chat, "AT^SGPSC=\"Engine\",2", sgpsc_prefix,
@@ -172,6 +188,205 @@ static void gemalto_location_reporting_enable(struct 
ofono_location_reporting *l
        g_free(cbd);
 }
 
+static void gemalto_location_reporting_get_properties(
+                       struct ofono_location_reporting *lr, void *_dict)
+{
+       DBusMessageIter *dict = _dict;
+       struct gps_data *gd = ofono_location_reporting_get_data(lr);
+       int property_pos = 0;
+       struct ofono_modem *modem = ofono_location_reporting_get_modem(lr);
+       const char *port = ofono_modem_get_string(modem, "GNSS");
+       const char *prop_port = "Port";
+
+       while(property_pos < MAX_GNSS_PROPERTIES &&
+                                       *gd->properties[property_pos].name) {
+               char *propval = gd->properties[property_pos].value_s;
+               ofono_dbus_dict_append(dict, gd->properties[property_pos].name,
+                                               DBUS_TYPE_STRING, &propval);
+               property_pos++;
+       }
+
+       /* add port outside of the modem property list */
+       DBG("%s=%s", prop_port, port);
+       if (port)
+               ofono_dbus_dict_append(dict, prop_port, DBUS_TYPE_STRING, 
&port);
+}
+
+static void change_property_list(GAtResult *result, gpointer user_data) {
+       struct ofono_location_reporting *lr = user_data;
+       struct gps_data *gd = ofono_location_reporting_get_data(lr);
+       GAtResultIter iter;
+       int property_pos;
+       DBusConnection *conn = ofono_dbus_get_connection();
+       const char *path = __ofono_atom_get_path(lr->atom);
+
+       g_at_result_iter_init(&iter, result);
+
+       /* supported format: ^SGPSC: "Nmea/Output","off" */
+       while (g_at_result_iter_next(&iter, "^SGPSC:")) {
+               const char *name = "";
+               const char *val = "";
+
+               if (!g_at_result_iter_next_string(&iter, &name))
+                       continue;
+
+               /*
+                * skip the "Info" property:
+                * different line format and different usage
+                */
+               if (g_str_equal(name,"Info"))
+                       continue;
+
+               if (!g_at_result_iter_next_string(&iter, &val))
+                       continue;
+
+               for(property_pos = 0; property_pos < MAX_GNSS_PROPERTIES &&
+                          *gd->properties[property_pos].name; property_pos++) {
+
+                       if (!g_str_equal(gd->properties[property_pos].name,
+                                                                       name))
+                               continue;
+
+                       strncpy(gd->properties[property_pos].value_s,val,
+                                       MAX_GNSS_STRLEN);
+                       ofono_dbus_signal_property_changed(conn, path,
+                                       OFONO_LOCATION_REPORTING_INTERFACE,
+                                       name, DBUS_TYPE_STRING, &val);
+               }
+       }
+}
+
+static void gemalto_location_reporting_set_property_cb(gboolean ok,
+                                       GAtResult *result, gpointer user_data)
+{
+       struct ofono_location_reporting *lr = user_data;
+
+       if (!ok || !lr)
+               return;
+
+       change_property_list(result, lr);
+}
+
+static void *gemalto_location_reporting_set_property(
+                               struct ofono_location_reporting *lr, void *_msg)
+{
+       DBusMessage *msg = _msg;
+       struct gps_data *gd;
+       DBusMessageIter iter, var;
+       const char *name;
+       int property_pos;
+
+       DBG("");
+       if (!lr)
+               return __ofono_error_not_available(msg);
+
+       gd = ofono_location_reporting_get_data(lr);
+
+       if (!gd)
+               return __ofono_error_not_available(msg);
+
+       if (dbus_message_iter_init(msg, &iter) == FALSE)
+               return __ofono_error_invalid_args(msg);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_get_basic(&iter, &name);
+       dbus_message_iter_next(&iter);
+
+       if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+               return __ofono_error_invalid_args(msg);
+
+       dbus_message_iter_recurse(&iter, &var);
+
+       property_pos = 0;
+
+       while(property_pos < MAX_GNSS_PROPERTIES &&
+                                       *gd->properties[property_pos].name) {
+
+               if (g_str_equal(name, gd->properties[property_pos].name))
+               {
+                       char s[128];
+                       char *value;
+
+                       if (dbus_message_iter_get_arg_type(&var) !=
+                                                       DBUS_TYPE_STRING)
+                               return __ofono_error_invalid_args(msg);
+
+                       dbus_message_iter_get_basic(&var, &value);
+
+                       /* do not set the same value and do not notify change */
+                       if (g_str_equal(value,
+                                       gd->properties[property_pos].value_s))
+                               return __ofono_error_not_available(msg);
+
+                       sprintf(s, "AT^SGPSC=\"%s\",\"%s\"",name, value);
+                       DBG("setting %s", s);
+
+                       /* TODO: report value changed */
+
+                       if (g_at_chat_send(gd->chat, s, sgpsc_prefix,
+                                   gemalto_location_reporting_set_property_cb,
+                                                               lr, NULL) > 0)
+
+                               return dbus_message_new_method_return(msg);
+
+                       return __ofono_error_not_available(msg);
+               }
+               property_pos++;
+
+       }
+
+       return __ofono_error_invalid_args(msg);
+}
+
+static void build_property_list(GAtResult *result, gpointer user_data) {
+       struct ofono_location_reporting *lr = user_data;
+       struct gps_data *gd = ofono_location_reporting_get_data(lr);
+       GAtResultIter iter;
+       int property_pos = 0;
+
+       /* skip pre-set */
+       while (property_pos < MAX_GNSS_PROPERTIES &&
+                                       *gd->properties[property_pos].name)
+               property_pos++;
+
+       g_at_result_iter_init(&iter, result);
+
+       /* supported format: ^SGPSC: "Nmea/Output","off" */
+       while ((property_pos < MAX_GNSS_PROPERTIES) &&
+                               g_at_result_iter_next(&iter, "^SGPSC:")) {
+               const char *name = "";
+               const char *val = "";
+
+               if (!g_at_result_iter_next_string(&iter, &name))
+                       continue;
+
+               /*
+                * skip the "Info" property:
+                * different line format and different usage
+                */
+               if (g_str_equal(name,"Info"))
+                       continue;
+
+               if (!g_at_result_iter_next_string(&iter, &val))
+                       continue;
+
+               strncpy(gd->properties[property_pos].name, name,
+                                                       MAX_GNSS_STRLEN);
+               strncpy(gd->properties[property_pos].value_s, val,
+                                                       MAX_GNSS_STRLEN);
+               property_pos++;
+       }
+
+       property_pos = 0;
+
+       while ((property_pos < MAX_GNSS_PROPERTIES) &&
+                                       *gd->properties[property_pos].name) {
+               property_pos++;
+       }
+}
+
 static void gemalto_location_reporting_support_cb(gboolean ok, GAtResult 
*result,
                                                        gpointer user_data)
 {
@@ -179,13 +394,50 @@ static void 
gemalto_location_reporting_support_cb(gboolean ok, GAtResult *result
 
        if (!ok) {
                ofono_location_reporting_remove(lr);
-
                return;
        }
 
+       build_property_list(result, user_data);
        ofono_location_reporting_register(lr);
 }
 
+static void gemalto_locrep_stored(struct ofono_location_reporting *lr) {
+       struct ofono_modem *modem = ofono_location_reporting_get_modem(lr);
+       struct gps_data *gd = ofono_location_reporting_get_data(lr);
+       const char *vid = ofono_modem_get_string(modem, "Vendor");
+       const char *pid = ofono_modem_get_string(modem, "Model");
+       char store[32];
+       int index;
+       char *property, *value;
+       char key[32];
+       GKeyFile *f;
+       char *command;
+
+       sprintf(store,"%s-%s/location-reporting", vid, pid);
+       f = storage_open(NULL, store);
+
+       if (!f)
+               return;
+
+       for (index = 0; ; index++) {
+               sprintf(key, "property_%d", index);
+               property = g_key_file_get_string(f, "Properties", key, NULL);
+
+               sprintf(key, "value_%d", index);
+               value = g_key_file_get_string(f, "Properties", key, NULL);
+
+               if (!property || !value)
+                       break;
+
+               command = g_strdup_printf("AT^SGPSC=%s,%s", property, value);
+               DBG("setting GNSS property: %s", command);
+               g_at_chat_send(gd->chat, command, NULL, NULL, NULL, NULL);
+               free(command);
+       }
+
+       storage_close(NULL, store, f, FALSE);
+}
+
 static int gemalto_location_reporting_probe(struct ofono_location_reporting 
*lr,
                                                unsigned int vendor, void *data)
 {
@@ -193,24 +445,35 @@ static int gemalto_location_reporting_probe(struct 
ofono_location_reporting *lr,
        struct gps_data *gd;
 
        gd = g_try_new0(struct gps_data, 1);
+
        if (gd == NULL)
                return -ENOMEM;
 
        gd->chat = g_at_chat_clone(chat);
-
        ofono_location_reporting_set_data(lr, gd);
 
-       g_at_chat_send(gd->chat, "AT^SGPSC=?", sgpsc_prefix,
+       /*
+        * Storage of parameters is read-only.
+        * It is intended for preconfiguring the devide by tbe manufacturer.
+        * The parameters are stored by USB VID-PID, because
+        * they are not standardized, and each model can have its own set.
+        */
+       gemalto_locrep_stored(lr);
+
+       g_at_chat_send(gd->chat, "AT^SGPSC?", sgpsc_prefix,
                                        gemalto_location_reporting_support_cb,
                                        lr, NULL);
 
        return 0;
 }
 
-static void gemalto_location_reporting_remove(struct ofono_location_reporting 
*lr)
+static void gemalto_location_reporting_remove(struct ofono_location_reporting
+                                                                       *lr)
 {
        struct gps_data *gd = ofono_location_reporting_get_data(lr);
 
+       /* TODO: store modified params if [Settings] sync==yes */
+
        ofono_location_reporting_set_data(lr, NULL);
 
        g_at_chat_unref(gd->chat);
@@ -224,6 +487,8 @@ static struct ofono_location_reporting_driver driver = {
        .remove                 = gemalto_location_reporting_remove,
        .enable                 = gemalto_location_reporting_enable,
        .disable                = gemalto_location_reporting_disable,
+       .get_properties         = gemalto_location_reporting_get_properties,
+       .set_property           = gemalto_location_reporting_set_property
 };
 
 void gemalto_location_reporting_init()
-- 
2.17.1

_______________________________________________
ofono mailing list
[email protected]
https://lists.ofono.org/mailman/listinfo/ofono

Reply via email to