mirror of git://git.sysmocom.de/ofono
ublox: network-registration atom
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.
This commit is contained in:
parent
819f89f955
commit
b4b74f009f
|
@ -471,6 +471,7 @@ builtin_sources += drivers/atmodem/atutil.h \
|
|||
drivers/ubloxmodem/ubloxmodem.h \
|
||||
drivers/ubloxmodem/ubloxmodem.c \
|
||||
drivers/ubloxmodem/gprs-context.c \
|
||||
drivers/ubloxmodem/network-registration.c \
|
||||
drivers/ubloxmodem/netmon.c \
|
||||
drivers/ubloxmodem/lte.c
|
||||
|
||||
|
|
|
@ -0,0 +1,427 @@
|
|||
/*
|
||||
*
|
||||
* oFono - Open Source Telephony
|
||||
*
|
||||
* Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
|
||||
* Copyright (C) 2010 ST-Ericsson AB.
|
||||
* Copyright (C) 2019 Norrbonn 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);
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue