For uBlox modems, a bit of custom setup is required, but after that the
generic "atmodem" (27.007-compatible) method implementations are
sufficient.  This driver, therefore, just puts the custom probe method
into place and defers remaining functionality to the recently exported
atmodem implementations.
---
 drivers/ubloxmodem/network-registration.c | 425 ++++++++++++++++++++++
 drivers/ubloxmodem/ubloxmodem.c           |   2 +
 drivers/ubloxmodem/ubloxmodem.h           |   3 +
 3 files changed, 430 insertions(+)
 create mode 100644 drivers/ubloxmodem/network-registration.c

diff --git a/drivers/ubloxmodem/network-registration.c 
b/drivers/ubloxmodem/network-registration.c
new file mode 100644
index 00000000..cfd194a9
--- /dev/null
+++ b/drivers/ubloxmodem/network-registration.c
@@ -0,0 +1,425 @@
+/*
+ *
+ *  oFono - Open Source Telephony
+ *
+ *  Copyright (C) 2008-2011  Intel Corporation. All rights reserved.
+ *  Copyright (C) 2010  ST-Ericsson AB.
+ *
+ *  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
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <glib.h>
+
+#include <ofono/log.h>
+#include <ofono/modem.h>
+#include <ofono/netreg.h>
+
+#include "gatchat.h"
+#include "gatresult.h"
+
+#include "common.h"
+#include "ubloxmodem.h"
+#include "drivers/atmodem/vendor.h"
+
+#include "drivers/atmodem/network-registration.h"
+
+static const char *none_prefix[] = { NULL };
+static const char *cmer_prefix[] = { "+CMER:", NULL };
+static const char *ureg_prefix[] = { "+UREG:", NULL };
+
+struct netreg_data {
+       struct at_netreg_data at_data;
+
+       const struct ublox_model *model;
+};
+
+struct tech_query {
+       int status;
+       int lac;
+       int ci;
+       struct ofono_netreg *netreg;
+};
+
+static void ciev_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct at_netreg_data *nd = ofono_netreg_get_data(netreg);
+       int strength, ind;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CIEV:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &ind))
+               return;
+
+       if (ind != nd->signal_index)
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &strength))
+               return;
+
+       if (strength == nd->signal_invalid)
+               strength = -1;
+       else
+               strength = (strength * 100) / (nd->signal_max - nd->signal_min);
+
+       ofono_netreg_strength_notify(netreg, strength);
+}
+
+static gboolean notify_time(gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct at_netreg_data *nd = ofono_netreg_get_data(netreg);
+
+       nd->nitz_timeout = 0;
+
+       ofono_netreg_time_notify(netreg, &nd->time);
+
+       return FALSE;
+}
+
+static void ctzdst_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct at_netreg_data *nd = ofono_netreg_get_data(netreg);
+       int dst;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CTZDST:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &dst))
+               return;
+
+       DBG("dst %d", dst);
+
+       nd->time.dst = dst;
+
+       if (nd->nitz_timeout > 0) {
+               g_source_remove(nd->nitz_timeout);
+               nd->nitz_timeout = 0;
+       }
+
+       ofono_netreg_time_notify(netreg, &nd->time);
+}
+
+static void ctzv_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct at_netreg_data *nd = ofono_netreg_get_data(netreg);
+       int year, mon, mday, hour, min, sec;
+       const char *tz, *time;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CTZV:"))
+               return;
+
+       if (!g_at_result_iter_next_unquoted_string(&iter, &tz))
+               return;
+
+       if (!g_at_result_iter_next_string(&iter, &time))
+               return;
+
+       DBG("tz %s time %s", tz, time);
+
+       if (sscanf(time, "%u/%u/%u,%u:%u:%u", &year, &mon, &mday,
+                                               &hour, &min, &sec) != 6)
+               return;
+
+       nd->time.sec = sec;
+       nd->time.min = min;
+       nd->time.hour = hour;
+       nd->time.mday = mday;
+       nd->time.mon = mon;
+       nd->time.year = 2000 + year;
+
+       nd->time.utcoff = atoi(tz) * 15 * 60;
+
+       /* Delay notification in case there's a DST update coming */
+       if (nd->nitz_timeout > 0)
+               g_source_remove(nd->nitz_timeout);
+
+       nd->nitz_timeout = g_timeout_add_seconds(1, notify_time, user_data);
+}
+
+static void ctze_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct at_netreg_data *nd = ofono_netreg_get_data(netreg);
+       int year, mon, mday, hour, min, sec;
+       int dst;
+       const char *tz, *time;
+       GAtResultIter iter;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+CTZE:"))
+               return;
+
+       if (!g_at_result_iter_next_unquoted_string(&iter, &tz))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &dst))
+               return;
+
+       if (!g_at_result_iter_next_string(&iter, &time))
+               return;
+
+       DBG("tz %s dst %d time %s", tz, dst, time);
+
+       if (sscanf(time, "%u/%u/%u,%u:%u:%u", &year, &mon, &mday,
+                                               &hour, &min, &sec) != 6)
+               return;
+
+       nd->time.sec = sec;
+       nd->time.min = min;
+       nd->time.hour = hour;
+       nd->time.mday = mday;
+       nd->time.mon = mon;
+       nd->time.year = 2000 + year;
+
+       nd->time.utcoff = atoi(tz) * 15 * 60;
+       nd->time.dst = dst;
+
+       ofono_netreg_time_notify(netreg, &nd->time);
+}
+
+
+static void ublox_query_tech_cb(gboolean ok, GAtResult *result, gpointer 
user_data)
+{
+       struct tech_query *tq = user_data;
+       GAtResultIter iter;
+       gint enabled, state;
+       int tech = -1;
+
+       if (!ok)
+               goto error;
+
+       g_at_result_iter_init(&iter, result);
+
+       if (!g_at_result_iter_next(&iter, "+UREG:"))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &enabled))
+               return;
+
+       if (!g_at_result_iter_next_number(&iter, &state))
+               return;
+
+       switch (state) {
+       case 4:
+               tech = 5;
+               break;
+       case 5:
+               tech = 4;
+               break;
+       case 8:
+               tech = 1;
+               break;
+       case 9:
+               tech = 2;
+               break;
+       default:
+               tech = state;
+       }
+
+error:
+       ofono_netreg_status_notify(tq->netreg,
+                       tq->status, tq->lac, tq->ci, tech);
+}
+
+static void creg_notify(GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       int status, lac, ci, tech;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+       struct tech_query *tq;
+
+       if (at_util_parse_reg_unsolicited(result, "+CREG:", &status,
+                       &lac, &ci, &tech, OFONO_VENDOR_GENERIC) == FALSE)
+               return;
+
+       if (status != 1 && status != 5)
+               goto notify;
+
+       if (ublox_is_toby_l4(nd->model)) {
+               tq = g_new0(struct tech_query, 1);
+
+               tq->status = status;
+               tq->lac = lac;
+               tq->ci = ci;
+               tq->netreg = netreg;
+
+               if (g_at_chat_send(nd->at_data.chat, "AT+UREG?", ureg_prefix,
+                               ublox_query_tech_cb, tq, g_free) > 0)
+                       return;
+
+               g_free(tq);
+       }
+
+       if ((status == 1 || status == 5) && tech == -1)
+               tech = nd->at_data.tech;
+
+notify:
+       ofono_netreg_status_notify(netreg, status, lac, ci, tech);
+}
+
+static void at_cmer_not_supported(struct ofono_netreg *netreg)
+{
+       ofono_error("+CMER not supported by this modem.  If this is an error"
+                       " please submit patches to support this hardware");
+
+       ofono_netreg_remove(netreg);
+}
+
+static void ublox_cmer_set_cb(gboolean ok,
+                               GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct at_netreg_data *nd = ofono_netreg_get_data(netreg);
+
+       if (!ok) {
+               at_cmer_not_supported(netreg);
+               return;
+       }
+
+       g_at_chat_register(nd->chat, "+CIEV:",
+                       ciev_notify, FALSE, netreg, NULL);
+
+       g_at_chat_register(nd->chat, "+CREG:",
+                               creg_notify, FALSE, netreg, NULL);
+
+       ofono_netreg_register(netreg);
+}
+
+static void ublox_creg_set_cb(gboolean ok,
+                               GAtResult *result, gpointer user_data)
+{
+       struct ofono_netreg *netreg = user_data;
+       struct netreg_data *nd = ofono_netreg_get_data(netreg);
+
+       if (!ok) {
+               ofono_error("Unable to initialize Network Registration");
+               ofono_netreg_remove(netreg);
+               return;
+       }
+
+       if (ublox_is_toby_l4(nd->model)) {
+               /* FIXME */
+               ofono_error("TOBY L4 requires polling of ECSQ");
+               ofono_error("TOBY L4 wants UREG notifications for tech 
updates");
+       }
+
+       /* Register for network time update reports */
+       if (ublox_is_toby_l2(nd->model)) {
+               /* TOBY L2 does not support CTZDST */
+               g_at_chat_register(nd->at_data.chat, "+CTZE:", ctze_notify,
+                                               FALSE, netreg, NULL);
+               g_at_chat_send(nd->at_data.chat, "AT+CTZR=2", none_prefix,
+                                               NULL, NULL, NULL);
+       } else {
+               g_at_chat_register(nd->at_data.chat, "+CTZV:", ctzv_notify,
+                                               FALSE, netreg, NULL);
+               g_at_chat_register(nd->at_data.chat, "+CTZDST:", ctzdst_notify,
+                                               FALSE, netreg, NULL);
+               g_at_chat_send(nd->at_data.chat, "AT+CTZR=1", none_prefix,
+                                               NULL, NULL, NULL);
+       }
+
+       /* AT+CMER NOTES:
+        * - For all u-blox models, mode 3 is equivalent to mode 1;
+        * since some models do not support setting modes 2 nor 3
+        * (see UBX-13002752), we prefer mode 1 for all models.
+        * - The TOBY L4 does not support ind=2
+        */
+       g_at_chat_send(nd->at_data.chat, "AT+CMER=1,0,0,1", cmer_prefix,
+                       ublox_cmer_set_cb, netreg, NULL);
+}
+
+/*
+ * uBlox netreg atom probe.
+ * - takes uBlox model ID parameter instead of AT vendor ID
+ */
+static int ublox_netreg_probe(struct ofono_netreg *netreg,
+                               unsigned int model_id,
+                               void *data)
+{
+       GAtChat *chat = data;
+       struct netreg_data *nd;
+
+       nd = g_new0(struct netreg_data, 1);
+
+       nd->model = ublox_model_from_id(model_id);
+
+       /* There should be no uBlox-specific quirks in the 'generic'
+        * AT driver
+        */
+       nd->at_data.vendor = OFONO_VENDOR_GENERIC;
+
+       nd->at_data.chat = g_at_chat_clone(chat);
+       nd->at_data.tech = -1;
+       nd->at_data.time.sec = -1;
+       nd->at_data.time.min = -1;
+       nd->at_data.time.hour = -1;
+       nd->at_data.time.mday = -1;
+       nd->at_data.time.mon = -1;
+       nd->at_data.time.year = -1;
+       nd->at_data.time.dst = 0;
+       nd->at_data.time.utcoff = 0;
+       ofono_netreg_set_data(netreg, nd);
+
+       /* All uBlox devices support n=2 so no need to query this */
+       g_at_chat_send(nd->at_data.chat, "AT+CREG=2", none_prefix,
+                       ublox_creg_set_cb, netreg, NULL);
+
+       return 0;
+}
+
+static const struct ofono_netreg_driver driver = {
+       .name                           = "ubloxmodem",
+       .probe                          = ublox_netreg_probe,
+       .remove                         = at_netreg_remove,
+       .registration_status            = at_registration_status,
+       .current_operator               = at_current_operator,
+       .list_operators                 = at_list_operators,
+       .register_auto                  = at_register_auto,
+       .register_manual                = at_register_manual,
+       .strength                       = at_signal_strength,
+};
+
+void ublox_netreg_init(void)
+{
+       ofono_netreg_driver_register(&driver);
+}
+
+void ublox_netreg_exit(void)
+{
+       ofono_netreg_driver_unregister(&driver);
+}
diff --git a/drivers/ubloxmodem/ubloxmodem.c b/drivers/ubloxmodem/ubloxmodem.c
index a52a67ea..719c77a0 100644
--- a/drivers/ubloxmodem/ubloxmodem.c
+++ b/drivers/ubloxmodem/ubloxmodem.c
@@ -115,6 +115,7 @@ int ublox_is_toby_l4(const struct ublox_model *model)
 static int ubloxmodem_init(void)
 {
        ublox_gprs_context_init();
+       ublox_netreg_init();
        ublox_netmon_init();
        ublox_lte_init();
 
@@ -124,6 +125,7 @@ static int ubloxmodem_init(void)
 static void ubloxmodem_exit(void)
 {
        ublox_gprs_context_exit();
+       ublox_netreg_exit();
        ublox_netmon_exit();
        ublox_lte_exit();
 }
diff --git a/drivers/ubloxmodem/ubloxmodem.h b/drivers/ubloxmodem/ubloxmodem.h
index 2c5b7433..81fe9481 100644
--- a/drivers/ubloxmodem/ubloxmodem.h
+++ b/drivers/ubloxmodem/ubloxmodem.h
@@ -43,6 +43,9 @@ int ublox_is_toby_l4(const struct ublox_model *model);
 extern void ublox_gprs_context_init(void);
 extern void ublox_gprs_context_exit(void);
 
+void ublox_netreg_init(void);
+void ublox_netreg_exit(void);
+
 extern void ublox_netmon_init(void);
 extern void ublox_netmon_exit(void);
 
-- 
2.20.1

_______________________________________________
ofono mailing list
ofono@ofono.org
https://lists.ofono.org/mailman/listinfo/ofono

Reply via email to