Merge sysmocom/lynxis/master into sysmocom/osmo-gsm-tester

Conflicts: plugins/gobi.c
This commit is contained in:
Alexander Couzens 2017-09-12 13:39:17 +02:00
commit 1056147002
49 changed files with 1466 additions and 88 deletions

View File

@ -123,3 +123,5 @@ Piotr Haber <gluedig@gmail.com>
André Draszik <git@andred.net> André Draszik <git@andred.net>
Lukasz Nowak <lnowak@tycoint.com> Lukasz Nowak <lnowak@tycoint.com>
Jonas Bonn <jonas@southpole.se> Jonas Bonn <jonas@southpole.se>
Matthijs Kooijman <matthijs@stdin.nl>
Clayton Craft <clayton@craftyguy.net>

View File

@ -236,7 +236,8 @@ builtin_sources += $(qmi_sources) \
drivers/qmimodem/gprs.c \ drivers/qmimodem/gprs.c \
drivers/qmimodem/gprs-context.c \ drivers/qmimodem/gprs-context.c \
drivers/qmimodem/radio-settings.c \ drivers/qmimodem/radio-settings.c \
drivers/qmimodem/location-reporting.c drivers/qmimodem/location-reporting.c \
drivers/qmimodem/qmibridge.c
builtin_modules += gobi builtin_modules += gobi
builtin_sources += plugins/gobi.c builtin_sources += plugins/gobi.c
@ -267,7 +268,8 @@ builtin_sources += drivers/atmodem/atmodem.h \
drivers/atmodem/gprs.c \ drivers/atmodem/gprs.c \
drivers/atmodem/gprs-context.c \ drivers/atmodem/gprs-context.c \
drivers/atmodem/sim-auth.c \ drivers/atmodem/sim-auth.c \
drivers/atmodem/gnss.c drivers/atmodem/gnss.c \
drivers/atmodem/lte.c
builtin_modules += nwmodem builtin_modules += nwmodem
builtin_sources += drivers/atmodem/atutil.h \ builtin_sources += drivers/atmodem/atutil.h \
@ -398,6 +400,11 @@ builtin_sources += drivers/atmodem/atutil.h \
drivers/gemaltomodem/gemaltomodem.c \ drivers/gemaltomodem/gemaltomodem.c \
drivers/gemaltomodem/location-reporting.c drivers/gemaltomodem/location-reporting.c
builtin_modules += xmm7modem
builtin_sources += drivers/atmodem/atutil.h \
drivers/xmm7modem/xmm7modem.h \
drivers/xmm7modem/xmm7modem.c \
drivers/xmm7modem/radio-settings.c
if PHONESIM if PHONESIM
builtin_modules += phonesim builtin_modules += phonesim
@ -623,10 +630,11 @@ src_ofonod_SOURCES = $(builtin_sources) $(gatchat_sources) src/ofono.ver \
src/cdma-provision.c src/handsfree.c \ src/cdma-provision.c src/handsfree.c \
src/handsfree-audio.c src/bluetooth.h \ src/handsfree-audio.c src/bluetooth.h \
src/hfp.h src/siri.c \ src/hfp.h src/siri.c \
src/netmon.c src/lte.c src/call-list.c src/netmon.c src/lte.c src/call-list.c \
src/netmonagent.c src/netmonagent.h
src_ofonod_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \ src_ofonod_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \
@GLIB_LIBS@ @DBUS_LIBS@ -ldl @GLIB_LIBS@ @DBUS_LIBS@ -ldl -lqmi-glib
src_ofonod_LDFLAGS = -Wl,--export-dynamic \ src_ofonod_LDFLAGS = -Wl,--export-dynamic \
-Wl,--version-script=$(srcdir)/src/ofono.ver -Wl,--version-script=$(srcdir)/src/ofono.ver
@ -649,7 +657,7 @@ AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ $(builtin_cflags) \
AM_CPPFLAGS = -I$(builddir)/include -I$(builddir)/src -I$(srcdir)/src \ AM_CPPFLAGS = -I$(builddir)/include -I$(builddir)/src -I$(srcdir)/src \
-I$(srcdir)/gdbus -I$(srcdir)/gisi -I$(srcdir)/gatchat \ -I$(srcdir)/gdbus -I$(srcdir)/gisi -I$(srcdir)/gatchat \
-I$(srcdir)/btio -I$(srcdir)/gril -I$(srcdir)/btio -I$(srcdir)/gril -I/usr/include/libqmi-glib
doc_files = doc/overview.txt doc/ofono-paper.txt doc/release-faq.txt \ doc_files = doc/overview.txt doc/ofono-paper.txt doc/release-faq.txt \
doc/manager-api.txt doc/modem-api.txt doc/network-api.txt \ doc/manager-api.txt doc/modem-api.txt doc/network-api.txt \
@ -776,7 +784,9 @@ test_scripts = test/backtrace \
test/list-allowed-access-points \ test/list-allowed-access-points \
test/enable-throttling \ test/enable-throttling \
test/disable-throttling \ test/disable-throttling \
test/set-lte-property test/set-lte-property \
test/test-serving-cell-info
if TEST if TEST
testdir = $(pkglibdir)/test testdir = $(pkglibdir)/test

View File

@ -22,6 +22,34 @@ Methods a{sv} GetServingCellInformation()
are available, their valid value ranges and are available, their valid value ranges and
applicability to different cell types. applicability to different cell types.
void RegisterAgent(object path)
Registers an agent which will be called whenever the
modem registers to or moves to a new cell.
void UnregisterAgent(object path)
Unregisters an agent.
NetworkMonitorAgent Hierarchy [experimental]
=============================
Service unique name
Interface org.ofono.NetworkMonitorAgent
Object path freely definable
Methods void ServingCellInformationChanged(a{sv}) [noreply]
This method is called whenever the serving cell
information has been updated.
Possible Errors: None
void Release() [noreply]
Agent is being released, possibly because of oFono
terminating, NetworkMonitor interface is being torn
down or modem off. No UnregisterAgent call is needed.
Network Monitor Property Types Network Monitor Property Types
============================== ==============================
@ -77,7 +105,7 @@ byte TimingAdvance [optional, gsm]
Contains the Timing Advance. Valid range of values is 0-219. Contains the Timing Advance. Valid range of values is 0-219.
byte Strength [optional, gsm, umts] byte Strength [optional, gsm, umts, lte]
Contains the signal strength. Valid values are 0-31. Refer to <rssi> Contains the signal strength. Valid values are 0-31. Refer to <rssi>
in 27.007, Section 8.5. in 27.007, Section 8.5.

View File

@ -52,6 +52,7 @@ static int atmodem_init(void)
at_gprs_context_init(); at_gprs_context_init();
at_sim_auth_init(); at_sim_auth_init();
at_gnss_init(); at_gnss_init();
at_lte_init();
return 0; return 0;
} }
@ -76,6 +77,7 @@ static void atmodem_exit(void)
at_gprs_exit(); at_gprs_exit();
at_gprs_context_exit(); at_gprs_context_exit();
at_gnss_exit(); at_gnss_exit();
at_lte_exit();
} }
OFONO_PLUGIN_DEFINE(atmodem, "AT modem driver", VERSION, OFONO_PLUGIN_DEFINE(atmodem, "AT modem driver", VERSION,

View File

@ -74,3 +74,6 @@ extern void at_sim_auth_exit(void);
extern void at_gnss_init(void); extern void at_gnss_init(void);
extern void at_gnss_exit(void); extern void at_gnss_exit(void);
extern void at_lte_init(void);
extern void at_lte_exit(void);

View File

@ -51,9 +51,7 @@ enum at_util_charset {
typedef void (*at_util_sim_inserted_cb_t)(gboolean present, void *userdata); typedef void (*at_util_sim_inserted_cb_t)(gboolean present, void *userdata);
void decode_at_error(struct ofono_error *error, const char *final); void decode_at_error(struct ofono_error *error, const char *final);
gint ofono_call_compare_by_status(gconstpointer a, gconstpointer b);
gint at_util_call_compare_by_phone_number(gconstpointer a, gconstpointer b); gint at_util_call_compare_by_phone_number(gconstpointer a, gconstpointer b);
gint ofono_call_compare_by_id(gconstpointer a, gconstpointer b);
GSList *at_util_parse_clcc(GAtResult *result, unsigned int *mpty_ids); GSList *at_util_parse_clcc(GAtResult *result, unsigned int *mpty_ids);
gboolean at_util_parse_reg(GAtResult *result, const char *prefix, gboolean at_util_parse_reg(GAtResult *result, const char *prefix,
int *mode, int *status, int *mode, int *status,

View File

@ -43,7 +43,7 @@
#include "atmodem.h" #include "atmodem.h"
#include "vendor.h" #include "vendor.h"
#define TUN_SYSFS_DIR "/sys/devices/virtual/misc/tun" #define TUN_DEV "/dev/net/tun"
#define STATIC_IP_NETMASK "255.255.255.255" #define STATIC_IP_NETMASK "255.255.255.255"
@ -426,7 +426,7 @@ static int at_gprs_context_probe(struct ofono_gprs_context *gc,
DBG(""); DBG("");
if (stat(TUN_SYSFS_DIR, &st) < 0) { if (stat(TUN_DEV, &st) < 0) {
ofono_error("Missing support for TUN/TAP devices"); ofono_error("Missing support for TUN/TAP devices");
return -ENODEV; return -ENODEV;
} }

View File

@ -327,6 +327,26 @@ static void huawei_mode_notify(GAtResult *result, gpointer user_data)
ofono_gprs_bearer_notify(gprs, bearer); ofono_gprs_bearer_notify(gprs, bearer);
} }
static void huawei_hcsq_notify(GAtResult *result, gpointer user_data)
{
struct ofono_gprs *gprs = user_data;
GAtResultIter iter;
const char *mode;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "^HCSQ:"))
return;
if (!g_at_result_iter_next_string(&iter, &mode))
return;
if (!strcmp("LTE", mode))
ofono_gprs_bearer_notify(gprs, 7); /* LTE */
/* in other modes, notification ^MODE is used */
}
static void telit_mode_notify(GAtResult *result, gpointer user_data) static void telit_mode_notify(GAtResult *result, gpointer user_data)
{ {
struct ofono_gprs *gprs = user_data; struct ofono_gprs *gprs = user_data;
@ -432,6 +452,8 @@ static void gprs_initialized(gboolean ok, GAtResult *result, gpointer user_data)
case OFONO_VENDOR_HUAWEI: case OFONO_VENDOR_HUAWEI:
g_at_chat_register(gd->chat, "^MODE:", huawei_mode_notify, g_at_chat_register(gd->chat, "^MODE:", huawei_mode_notify,
FALSE, gprs, NULL); FALSE, gprs, NULL);
g_at_chat_register(gd->chat, "^HCSQ:", huawei_hcsq_notify,
FALSE, gprs, NULL);
break; break;
case OFONO_VENDOR_UBLOX: case OFONO_VENDOR_UBLOX:
case OFONO_VENDOR_UBLOX_TOBY_L2: case OFONO_VENDOR_UBLOX_TOBY_L2:

142
drivers/atmodem/lte.c Normal file
View File

@ -0,0 +1,142 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 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 <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <glib.h>
#include <ofono/modem.h>
#include <ofono/gprs-context.h>
#include <ofono/log.h>
#include <ofono/lte.h>
#include "gatchat.h"
#include "gatresult.h"
#include "atmodem.h"
struct lte_driver_data {
GAtChat *chat;
};
static void at_lte_set_default_attach_info_cb(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_lte_cb_t cb = cbd->cb;
struct ofono_error error;
DBG("ok %d", ok);
decode_at_error(&error, g_at_result_final_response(result));
cb(&error, cbd->data);
}
static void at_lte_set_default_attach_info(const struct ofono_lte *lte,
const struct ofono_lte_default_attach_info *info,
ofono_lte_cb_t cb, void *data)
{
struct lte_driver_data *ldd = ofono_lte_get_data(lte);
char buf[32 + OFONO_GPRS_MAX_APN_LENGTH + 1];
struct cb_data *cbd = cb_data_new(cb, data);
DBG("LTE config with APN: %s", info->apn);
if (strlen(info->apn) > 0)
snprintf(buf, sizeof(buf), "AT+CGDCONT=0,\"IP\",\"%s\"",
info->apn);
else
snprintf(buf, sizeof(buf), "AT+CGDCONT=0,\"IP\"");
/* We can't do much in case of failure so don't check response. */
if (g_at_chat_send(ldd->chat, buf, NULL,
at_lte_set_default_attach_info_cb, cbd, g_free) > 0)
return;
CALLBACK_WITH_FAILURE(cb, data);
}
static gboolean lte_delayed_register(gpointer user_data)
{
struct ofono_lte *lte = user_data;
ofono_lte_register(lte);
return FALSE;
}
static int at_lte_probe(struct ofono_lte *lte, void *data)
{
GAtChat *chat = data;
struct lte_driver_data *ldd;
DBG("at lte probe");
ldd = g_try_new0(struct lte_driver_data, 1);
if (!ldd)
return -ENOMEM;
ldd->chat = g_at_chat_clone(chat);
ofono_lte_set_data(lte, ldd);
g_idle_add(lte_delayed_register, lte);
return 0;
}
static void at_lte_remove(struct ofono_lte *lte)
{
struct lte_driver_data *ldd = ofono_lte_get_data(lte);
DBG("at lte remove");
g_at_chat_unref(ldd->chat);
ofono_lte_set_data(lte, NULL);
g_free(ldd);
}
static struct ofono_lte_driver driver = {
.name = "atmodem",
.probe = at_lte_probe,
.remove = at_lte_remove,
.set_default_attach_info = at_lte_set_default_attach_info,
};
void at_lte_init(void)
{
ofono_lte_driver_register(&driver);
}
void at_lte_exit(void)
{
ofono_lte_driver_unregister(&driver);
}

View File

@ -1088,6 +1088,27 @@ static void huawei_mode_notify(GAtResult *result, gpointer user_data)
} }
} }
static void huawei_hcsq_notify(GAtResult *result, gpointer user_data)
{
struct ofono_netreg *netreg = user_data;
struct netreg_data *nd = ofono_netreg_get_data(netreg);
GAtResultIter iter;
const char *mode;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "^HCSQ:"))
return;
if (!g_at_result_iter_next_string(&iter, &mode))
return;
if (!strcmp("LTE", mode))
nd->tech = ACCESS_TECHNOLOGY_EUTRAN;
/* for other technologies, notification ^MODE is used */
}
static void huawei_nwtime_notify(GAtResult *result, gpointer user_data) static void huawei_nwtime_notify(GAtResult *result, gpointer user_data)
{ {
struct ofono_netreg *netreg = user_data; struct ofono_netreg *netreg = user_data;
@ -1896,6 +1917,10 @@ static void at_creg_set_cb(gboolean ok, GAtResult *result, gpointer user_data)
g_at_chat_register(nd->chat, "^MODE:", huawei_mode_notify, g_at_chat_register(nd->chat, "^MODE:", huawei_mode_notify,
FALSE, netreg, NULL); FALSE, netreg, NULL);
/* Register for 4G system mode reports */
g_at_chat_register(nd->chat, "^HCSQ:", huawei_hcsq_notify,
FALSE, netreg, NULL);
/* Register for network time reports */ /* Register for network time reports */
g_at_chat_register(nd->chat, "^NWTIME:", huawei_nwtime_notify, g_at_chat_register(nd->chat, "^NWTIME:", huawei_nwtime_notify,
FALSE, netreg, NULL); FALSE, netreg, NULL);

View File

@ -43,7 +43,7 @@
#include "cdmamodem.h" #include "cdmamodem.h"
#include "drivers/atmodem/vendor.h" #include "drivers/atmodem/vendor.h"
#define TUN_SYSFS_DIR "/sys/devices/virtual/misc/tun" #define TUN_DEV "/dev/net/tun"
#define STATIC_IP_NETMASK "255.255.255.255" #define STATIC_IP_NETMASK "255.255.255.255"
@ -285,7 +285,7 @@ static int cdma_connman_probe(struct ofono_cdma_connman *cm,
DBG(""); DBG("");
if (stat(TUN_SYSFS_DIR, &st) < 0) { if (stat(TUN_DEV, &st) < 0) {
ofono_error("Missing support for TUN/TAP devices"); ofono_error("Missing support for TUN/TAP devices");
return -ENODEV; return -ENODEV;
} }

View File

@ -42,7 +42,7 @@
#include "ifxmodem.h" #include "ifxmodem.h"
#define TUN_SYSFS_DIR "/sys/devices/virtual/misc/tun" #define TUN_DEV "/dev/net/tun"
#define STATIC_IP_NETMASK "255.255.255.255" #define STATIC_IP_NETMASK "255.255.255.255"
@ -470,7 +470,7 @@ static int ifx_gprs_context_probe(struct ofono_gprs_context *gc,
DBG(""); DBG("");
if (stat(TUN_SYSFS_DIR, &st) < 0) { if (stat(TUN_DEV, &st) < 0) {
ofono_error("Missing support for TUN/TAP devices"); ofono_error("Missing support for TUN/TAP devices");
return -ENODEV; return -ENODEV;
} }

View File

@ -646,8 +646,31 @@ error:
/* ISI callback: PIN state (enabled/disabled) query */ /* ISI callback: PIN state (enabled/disabled) query */
static void sec_code_state_resp_cb(const GIsiMessage *msg, void *opaque) static void sec_code_state_resp_cb(const GIsiMessage *msg, void *opaque)
{ {
check_sec_response(msg, opaque, SEC_CODE_STATE_OK_RESP, struct isi_cb_data *cbd = opaque;
SEC_CODE_STATE_FAIL_RESP); ofono_query_facility_lock_cb_t cb = cbd->cb;
int locked;
uint8_t state;
uint8_t status;
if (!g_isi_msg_data_get_byte(msg, 0, &state) ||
!g_isi_msg_data_get_byte(msg, 1, &status))
goto error;
if (state != SEC_CODE_STATE_OK_RESP)
goto error;
if (status == SEC_CODE_ENABLE)
locked = 1;
else if (status == SEC_CODE_DISABLE)
locked = 0;
else
goto error;
CALLBACK_WITH_SUCCESS(cb, locked, cbd->data);
return;
error:
CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
} }
static void isi_query_locked(struct ofono_sim *sim, static void isi_query_locked(struct ofono_sim *sim,

View File

@ -35,6 +35,8 @@
#define QMI_NAS_SS_INFO_IND 36 /* Current serving system info indication */ #define QMI_NAS_SS_INFO_IND 36 /* Current serving system info indication */
#define QMI_NAS_GET_HOME_INFO 37 /* Get info about home network */ #define QMI_NAS_GET_HOME_INFO 37 /* Get info about home network */
#define QMI_NAS_SET_SYSTEM_SELECTION_PREF 51
#define QMI_NAS_GET_SYSTEM_SELECTION_PREF 52
/* Set NAS state report conditions */ /* Set NAS state report conditions */
#define QMI_NAS_PARAM_REPORT_SIGNAL_STRENGTH 0x10 #define QMI_NAS_PARAM_REPORT_SIGNAL_STRENGTH 0x10
@ -176,4 +178,13 @@ struct qmi_nas_home_network {
char desc[0]; char desc[0];
} __attribute__((__packed__)); } __attribute__((__packed__));
#define QMI_NAS_RAT_MODE_PREF_ANY (-1)
#define QMI_NAS_RAT_MODE_PREF_GSM (1 << 2)
#define QMI_NAS_RAT_MODE_PREF_UMTS (1 << 3)
#define QMI_NAS_RAT_MODE_PREF_LTE (1 << 4)
#define QMI_NAS_PARAM_SYSTEM_SELECTION_PREF_MODE 0x11
#define QMI_NAS_RESULT_SYSTEM_SELECTION_PREF_MODE 0x11
int qmi_nas_rat_to_tech(uint8_t rat); int qmi_nas_rat_to_tech(uint8_t rat);

View File

@ -48,14 +48,14 @@ static bool extract_ss_info_time(
struct qmi_result *result, struct qmi_result *result,
struct ofono_network_time *time) struct ofono_network_time *time)
{ {
const struct qmi_nas_3gpp_time *time_3gpp= NULL; const struct qmi_nas_3gpp_time *time_3gpp = NULL;
uint8_t dst_3gpp; uint8_t dst_3gpp;
bool dst_3gpp_valid; bool dst_3gpp_valid;
uint16_t len; uint16_t len;
/* parse 3gpp time & dst */ /* parse 3gpp time & dst */
dst_3gpp_valid = qmi_result_get_uint8(result, QMI_NAS_RESULT_3GGP_DST, dst_3gpp_valid = qmi_result_get_uint8(result, QMI_NAS_RESULT_3GGP_DST,
&dst_3gpp); &dst_3gpp);
time_3gpp = qmi_result_get(result, QMI_NAS_RESULT_3GPP_TIME, &len); time_3gpp = qmi_result_get(result, QMI_NAS_RESULT_3GPP_TIME, &len);
if (time_3gpp && len == sizeof(struct qmi_nas_3gpp_time) && if (time_3gpp && len == sizeof(struct qmi_nas_3gpp_time) &&
@ -65,7 +65,7 @@ static bool extract_ss_info_time(
time->mday = time_3gpp->day; time->mday = time_3gpp->day;
time->hour = time_3gpp->hour; time->hour = time_3gpp->hour;
time->min = time_3gpp->minute; time->min = time_3gpp->minute;
time->sec= time_3gpp->second; time->sec = time_3gpp->second;
time->utcoff = time_3gpp->timezone * 15 * 60; time->utcoff = time_3gpp->timezone * 15 * 60;
time->dst = dst_3gpp; time->dst = dst_3gpp;
return true; return true;
@ -487,10 +487,11 @@ static void event_notify(struct qmi_result *result, void *user_data)
if (ss) { if (ss) {
int strength; int strength;
DBG("signal with %d dBm on %d", ss->dbm, ss->rat);
strength = dbm_to_strength(ss->dbm); strength = dbm_to_strength(ss->dbm);
DBG("signal with %d%%(%d dBm) on %d",
strength, ss->dbm, ss->rat);
ofono_netreg_strength_notify(netreg, strength); ofono_netreg_strength_notify(netreg, strength);
} }
@ -510,10 +511,17 @@ static void event_notify(struct qmi_result *result, void *user_data)
static void set_event_cb(struct qmi_result *result, void *user_data) static void set_event_cb(struct qmi_result *result, void *user_data)
{ {
struct ofono_netreg *netreg = user_data; struct ofono_netreg *netreg = user_data;
struct netreg_data *data = ofono_netreg_get_data(netreg);
DBG(""); DBG("");
ofono_netreg_register(netreg); ofono_netreg_register(netreg);
qmi_service_register(data->nas, QMI_NAS_EVENT,
event_notify, netreg, NULL);
qmi_service_register(data->nas, QMI_NAS_SS_INFO_IND,
ss_info_notify, netreg, NULL);
} }
static void create_nas_cb(struct qmi_service *service, void *user_data) static void create_nas_cb(struct qmi_service *service, void *user_data)
@ -535,12 +543,6 @@ static void create_nas_cb(struct qmi_service *service, void *user_data)
data->nas = qmi_service_ref(service); data->nas = qmi_service_ref(service);
qmi_service_register(data->nas, QMI_NAS_EVENT,
event_notify, netreg, NULL);
qmi_service_register(data->nas, QMI_NAS_SS_INFO_IND,
ss_info_notify, netreg, NULL);
param = qmi_param_new(); param = qmi_param_new();
if (!param) if (!param)
goto done; goto done;

View File

@ -37,6 +37,7 @@
#include <ofono/log.h> #include <ofono/log.h>
#include "qmibridge.h"
#include "qmi.h" #include "qmi.h"
#include "ctl.h" #include "ctl.h"
@ -651,6 +652,8 @@ static gboolean can_write_data(GIOChannel *channel, GIOCondition cond,
__debug_msg(' ', req->buf, bytes_written, __debug_msg(' ', req->buf, bytes_written,
device->debug_func, device->debug_data); device->debug_func, device->debug_data);
qmibridge_decode_req(req->buf, req->len);
hdr = req->buf; hdr = req->buf;
if (hdr->service == QMI_SERVICE_CONTROL) if (hdr->service == QMI_SERVICE_CONTROL)
@ -838,6 +841,7 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond,
__hexdump('<', buf, bytes_read, __hexdump('<', buf, bytes_read,
device->debug_func, device->debug_data); device->debug_func, device->debug_data);
qmibridge_decode_read(buf, bytes_read);
offset = 0; offset = 0;
while (offset < bytes_read) { while (offset < bytes_read) {
@ -1136,15 +1140,12 @@ static void discover_callback(uint16_t message, uint16_t length,
if (type == QMI_SERVICE_CONTROL) { if (type == QMI_SERVICE_CONTROL) {
device->control_major = major; device->control_major = major;
device->control_minor = minor; device->control_minor = minor;
continue;
} }
list[count].type = type; list[i].type = type;
list[count].major = major; list[i].major = major;
list[count].minor = minor; list[i].minor = minor;
list[count].name = name; list[i].name = name;
count++;
if (name) if (name)
__debug_device(device, "found service [%s %d.%d]", __debug_device(device, "found service [%s %d.%d]",
@ -1153,6 +1154,7 @@ static void discover_callback(uint16_t message, uint16_t length,
__debug_device(device, "found service [%d %d.%d]", __debug_device(device, "found service [%d %d.%d]",
type, major, minor); type, major, minor);
} }
count = service_list->count;
ptr = tlv_get(buffer, length, 0x10, &len); ptr = tlv_get(buffer, length, 0x10, &len);
if (!ptr) if (!ptr)
@ -1162,11 +1164,6 @@ static void discover_callback(uint16_t message, uint16_t length,
service_list = ptr + *((uint8_t *) ptr) + 1; service_list = ptr + *((uint8_t *) ptr) + 1;
for (i = 0; i < service_list->count; i++) {
if (service_list->services[i].type == QMI_SERVICE_CONTROL)
continue;
}
done: done:
device->version_list = list; device->version_list = list;
device->version_count = count; device->version_count = count;
@ -1767,6 +1764,27 @@ bool qmi_result_get_uint8(struct qmi_result *result, uint8_t type,
return true; return true;
} }
bool qmi_result_get_int16(struct qmi_result *result, uint8_t type,
int16_t *value)
{
const unsigned char *ptr;
uint16_t len, tmp;
if (!result || !type)
return false;
ptr = tlv_get(result->data, result->length, type, &len);
if (!ptr)
return false;
memcpy(&tmp, ptr, 2);
if (value)
*value = GINT16_FROM_LE(tmp);
return true;
}
bool qmi_result_get_uint16(struct qmi_result *result, uint8_t type, bool qmi_result_get_uint16(struct qmi_result *result, uint8_t type,
uint16_t *value) uint16_t *value)
{ {

View File

@ -136,6 +136,8 @@ const void *qmi_result_get(struct qmi_result *result, uint8_t type,
char *qmi_result_get_string(struct qmi_result *result, uint8_t type); char *qmi_result_get_string(struct qmi_result *result, uint8_t type);
bool qmi_result_get_uint8(struct qmi_result *result, uint8_t type, bool qmi_result_get_uint8(struct qmi_result *result, uint8_t type,
uint8_t *value); uint8_t *value);
bool qmi_result_get_int16(struct qmi_result *result, uint8_t type,
int16_t *value);
bool qmi_result_get_uint16(struct qmi_result *result, uint8_t type, bool qmi_result_get_uint16(struct qmi_result *result, uint8_t type,
uint16_t *value); uint16_t *value);
bool qmi_result_get_uint32(struct qmi_result *result, uint8_t type, bool qmi_result_get_uint32(struct qmi_result *result, uint8_t type,

View File

@ -0,0 +1,40 @@
#include <libqmi-glib.h>
#include <string.h>
#include <stdio.h>
#include <ofono/log.h>
#include <glib.h>
static void ask_qmi(const char *prefix, void *data, size_t len)
{
/* from osmo-qcdiag */
GByteArray *buffer;
GError *error = NULL;
QmiMessage *message;
gchar *printable;
buffer = g_byte_array_sized_new(len);
g_byte_array_append(buffer, data, len);
message = qmi_message_new_from_raw(buffer, &error);
if (!message) {
fprintf(stderr, "qmi_message_new_from_raw() returned NULL\n");
return;
}
printable = qmi_message_get_printable(message, "QMI ");
DBG("%s: %s", prefix, printable);
g_free(printable);
}
void qmibridge_decode_read(void *data, size_t len)
{
ask_qmi("READ", data, len);
}
void qmibridge_decode_req(void *data, size_t len)
{
ask_qmi("_REQ", data, len);
}

View File

@ -0,0 +1,3 @@
void qmibridge_decode_read(void *data, size_t len);
void qmibridge_decode_req(void *data, size_t len);

View File

@ -29,15 +29,202 @@
#include "qmi.h" #include "qmi.h"
#include "nas.h" #include "nas.h"
#include "dms.h"
#include "qmimodem.h" #include "qmimodem.h"
struct settings_data { struct settings_data {
struct qmi_service *nas; struct qmi_service *nas;
struct qmi_service *dms;
uint16_t major; uint16_t major;
uint16_t minor; uint16_t minor;
}; };
static void get_system_selection_pref_cb(struct qmi_result *result,
void* user_data)
{
struct cb_data *cbd = user_data;
ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
enum ofono_radio_access_mode mode = OFONO_RADIO_ACCESS_MODE_ANY;
uint16_t pref;
DBG("");
if (qmi_result_set_error(result, NULL)) {
CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
return;
}
qmi_result_get_uint16(result,
QMI_NAS_RESULT_SYSTEM_SELECTION_PREF_MODE, &pref);
switch (pref) {
case QMI_NAS_RAT_MODE_PREF_GSM:
mode = OFONO_RADIO_ACCESS_MODE_GSM;
break;
case QMI_NAS_RAT_MODE_PREF_UMTS:
mode = OFONO_RADIO_ACCESS_MODE_UMTS;
break;
case QMI_NAS_RAT_MODE_PREF_LTE:
mode = OFONO_RADIO_ACCESS_MODE_LTE;
break;
}
CALLBACK_WITH_SUCCESS(cb, mode, cbd->data);
}
static void qmi_query_rat_mode(struct ofono_radio_settings *rs,
ofono_radio_settings_rat_mode_query_cb_t cb,
void *user_data)
{
struct settings_data *data = ofono_radio_settings_get_data(rs);
struct cb_data *cbd = cb_data_new(cb, user_data);
DBG("");
if (qmi_service_send(data->nas,
QMI_NAS_GET_SYSTEM_SELECTION_PREF, NULL,
get_system_selection_pref_cb, cbd, g_free) > 0)
return;
CALLBACK_WITH_FAILURE(cb, -1, data);
}
static void set_system_selection_pref_cb(struct qmi_result *result,
void* user_data)
{
struct cb_data *cbd = user_data;
ofono_radio_settings_rat_mode_set_cb_t cb = cbd->cb;
DBG("");
if (qmi_result_set_error(result, NULL)) {
CALLBACK_WITH_FAILURE(cb, cbd->data);
return;
}
CALLBACK_WITH_SUCCESS(cb, cbd->data);
}
static void qmi_set_rat_mode(struct ofono_radio_settings *rs,
enum ofono_radio_access_mode mode,
ofono_radio_settings_rat_mode_set_cb_t cb,
void *user_data)
{
struct settings_data *data = ofono_radio_settings_get_data(rs);
struct cb_data *cbd = cb_data_new(cb, user_data);
uint16_t pref = QMI_NAS_RAT_MODE_PREF_ANY;
struct qmi_param *param;
DBG("");
switch (mode) {
case OFONO_RADIO_ACCESS_MODE_ANY:
pref = QMI_NAS_RAT_MODE_PREF_ANY;
break;
case OFONO_RADIO_ACCESS_MODE_GSM:
pref = QMI_NAS_RAT_MODE_PREF_GSM;
break;
case OFONO_RADIO_ACCESS_MODE_UMTS:
pref = QMI_NAS_RAT_MODE_PREF_UMTS;
break;
case OFONO_RADIO_ACCESS_MODE_LTE:
pref = QMI_NAS_RAT_MODE_PREF_LTE;
break;
}
param = qmi_param_new();
if (!param) {
CALLBACK_WITH_FAILURE(cb, user_data);
return;
}
qmi_param_append_uint16(param, QMI_NAS_PARAM_SYSTEM_SELECTION_PREF_MODE,
pref);
if (qmi_service_send(data->nas,
QMI_NAS_SET_SYSTEM_SELECTION_PREF, param,
set_system_selection_pref_cb, cbd, g_free) > 0)
return;
qmi_param_free(param);
CALLBACK_WITH_FAILURE(cb, user_data);
g_free(cbd);
}
static void get_caps_cb(struct qmi_result *result, void *user_data)
{
struct cb_data *cbd = user_data;
ofono_radio_settings_available_rats_query_cb_t cb = cbd->cb;
const struct qmi_dms_device_caps *caps;
unsigned int available_rats;
uint16_t len;
uint8_t i;
DBG("");
if (qmi_result_set_error(result, NULL))
goto error;
caps = qmi_result_get(result, QMI_DMS_RESULT_DEVICE_CAPS, &len);
if (!caps)
goto error;
available_rats = 0;
for (i = 0; i < caps->radio_if_count; i++) {
switch (caps->radio_if[i]) {
case QMI_DMS_RADIO_IF_GSM:
available_rats |= OFONO_RADIO_ACCESS_MODE_GSM;
break;
case QMI_DMS_RADIO_IF_UMTS:
available_rats |= OFONO_RADIO_ACCESS_MODE_UMTS;
break;
case QMI_DMS_RADIO_IF_LTE:
available_rats |= OFONO_RADIO_ACCESS_MODE_LTE;
break;
}
}
CALLBACK_WITH_SUCCESS(cb, available_rats, cbd->data);
return;
error:
CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
}
static void qmi_query_available_rats(struct ofono_radio_settings *rs,
ofono_radio_settings_available_rats_query_cb_t cb,
void *data)
{
struct settings_data *rsd = ofono_radio_settings_get_data(rs);
struct cb_data *cbd = cb_data_new(cb, data);
if (!rsd->dms)
goto error;
if (qmi_service_send(rsd->dms, QMI_DMS_GET_CAPS, NULL,
get_caps_cb, cbd, g_free) > 0)
return;
error:
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, -1, data);
}
static void create_dms_cb(struct qmi_service *service, void *user_data)
{
struct ofono_radio_settings *rs = user_data;
struct settings_data *data = ofono_radio_settings_get_data(rs);
DBG("");
if (!service)
return;
data->dms = qmi_service_ref(service);
}
static void create_nas_cb(struct qmi_service *service, void *user_data) static void create_nas_cb(struct qmi_service *service, void *user_data)
{ {
struct ofono_radio_settings *rs = user_data; struct ofono_radio_settings *rs = user_data;
@ -74,11 +261,12 @@ static int qmi_radio_settings_probe(struct ofono_radio_settings *rs,
ofono_radio_settings_set_data(rs, data); ofono_radio_settings_set_data(rs, data);
qmi_service_create_shared(device, QMI_SERVICE_DMS,
create_dms_cb, rs, NULL);
qmi_service_create_shared(device, QMI_SERVICE_NAS, qmi_service_create_shared(device, QMI_SERVICE_NAS,
create_nas_cb, rs, NULL); create_nas_cb, rs, NULL);
return 0; return 0;
} }
static void qmi_radio_settings_remove(struct ofono_radio_settings *rs) static void qmi_radio_settings_remove(struct ofono_radio_settings *rs)
@ -100,6 +288,9 @@ static struct ofono_radio_settings_driver driver = {
.name = "qmimodem", .name = "qmimodem",
.probe = qmi_radio_settings_probe, .probe = qmi_radio_settings_probe,
.remove = qmi_radio_settings_remove, .remove = qmi_radio_settings_remove,
.set_rat_mode = qmi_set_rat_mode,
.query_rat_mode = qmi_query_rat_mode,
.query_available_rats = qmi_query_available_rats,
}; };
void qmi_radio_settings_init(void) void qmi_radio_settings_init(void)

View File

@ -277,7 +277,7 @@ static void qmi_bearer_query(struct ofono_sms *sms,
DBG(""); DBG("");
if (data->major < 1 && data->minor < 2) if (data->major < 1 || (data->major == 1 && data->minor < 2))
goto error; goto error;
if (qmi_service_send(data->wms, QMI_WMS_GET_DOMAIN_PREF, NULL, if (qmi_service_send(data->wms, QMI_WMS_GET_DOMAIN_PREF, NULL,
@ -315,7 +315,7 @@ static void qmi_bearer_set(struct ofono_sms *sms, int bearer,
DBG("bearer %d", bearer); DBG("bearer %d", bearer);
if (data->major < 1 && data->minor < 2) if (data->major < 1 || (data->major == 1 && data->minor < 2))
goto error; goto error;
domain = bearer_to_domain(bearer); domain = bearer_to_domain(bearer);

View File

@ -65,6 +65,9 @@ static void all_call_status_ind(struct qmi_result *result, void *user_data)
int i; int i;
int size = 0; int size = 0;
struct qmi_voice_all_call_status_ind status_ind; struct qmi_voice_all_call_status_ind status_ind;
GSList *n, *o;
struct ofono_call *nc, *oc;
if (qmi_voice_ind_call_status(result, &status_ind) != NONE) { if (qmi_voice_ind_call_status(result, &status_ind) != NONE) {
DBG("Parsing of all call status indication failed"); DBG("Parsing of all call status indication failed");
@ -112,7 +115,7 @@ static void all_call_status_ind(struct qmi_result *result, void *user_data)
call->type = 0; /* always voice */ call->type = 0; /* always voice */
number_size = remote_party->number_size; number_size = remote_party->number_size;
if (number_size > OFONO_MAX_PHONE_NUMBER_LENGTH) if (number_size > OFONO_MAX_PHONE_NUMBER_LENGTH)
number_size = OFONO_MAX_PHONE_NUMBER_LENGTH; OFONO_MAX_PHONE_NUMBER_LENGTH;
strncpy(call->phone_number.number, remote_party->number, strncpy(call->phone_number.number, remote_party->number,
number_size); number_size);
/* FIXME: set phone_number_type */ /* FIXME: set phone_number_type */
@ -201,6 +204,7 @@ static void dial_cb(struct qmi_result *result, void *user_data)
ofono_voicecall_cb_t cb = cbd->cb; ofono_voicecall_cb_t cb = cbd->cb;
uint16_t error; uint16_t error;
struct qmi_voice_dial_call_result dial_result; struct qmi_voice_dial_call_result dial_result;
struct ofono_call *call;
if (qmi_result_set_error(result, &error)) { if (qmi_result_set_error(result, &error)) {
DBG("QMI Error %d", error); DBG("QMI Error %d", error);
@ -262,9 +266,11 @@ static void dial(struct ofono_voicecall *vc, const struct ofono_phone_number *ph
static void answer_cb(struct qmi_result *result, void *user_data) static void answer_cb(struct qmi_result *result, void *user_data)
{ {
struct cb_data *cbd = user_data; struct cb_data *cbd = user_data;
struct ofono_voicecall *vc = cbd->user;
ofono_voicecall_cb_t cb = cbd->cb; ofono_voicecall_cb_t cb = cbd->cb;
uint16_t error; uint16_t error;
struct qmi_voice_answer_call_result answer_result; struct qmi_voice_answer_call_result answer_result;
struct ofono_call *call;
if (qmi_result_set_error(result, &error)) { if (qmi_result_set_error(result, &error)) {
DBG("QMI Error %d", error); DBG("QMI Error %d", error);
@ -322,9 +328,11 @@ err:
static void end_cb(struct qmi_result *result, void *user_data) static void end_cb(struct qmi_result *result, void *user_data)
{ {
struct cb_data *cbd = user_data; struct cb_data *cbd = user_data;
struct ofono_voicecall *vc = cbd->user;
ofono_voicecall_cb_t cb = cbd->cb; ofono_voicecall_cb_t cb = cbd->cb;
uint16_t error; uint16_t error;
struct qmi_voice_end_call_result end_result; struct qmi_voice_end_call_result end_result;
struct ofono_call *call;
if (qmi_result_set_error(result, &error)) { if (qmi_result_set_error(result, &error)) {
DBG("QMI Error %d", error); DBG("QMI Error %d", error);
@ -347,6 +355,7 @@ static void release_specific(struct ofono_voicecall *vc, int id,
struct voicecall_data *vd = ofono_voicecall_get_data(vc); struct voicecall_data *vd = ofono_voicecall_get_data(vc);
struct cb_data *cbd = cb_data_new(cb, data); struct cb_data *cbd = cb_data_new(cb, data);
struct qmi_voice_end_call_arg arg; struct qmi_voice_end_call_arg arg;
int i;
DBG(""); DBG("");
cbd->user = vc; cbd->user = vc;
@ -369,19 +378,21 @@ static void hangup_active(struct ofono_voicecall *vc,
ofono_voicecall_cb_t cb, void *data) ofono_voicecall_cb_t cb, void *data)
{ {
struct voicecall_data *vd = ofono_voicecall_get_data(vc); struct voicecall_data *vd = ofono_voicecall_get_data(vc);
struct qmi_voice_end_call_arg arg;
struct ofono_call *call; struct ofono_call *call;
GSList *list = NULL; GSList *list = NULL;
enum call_status active[] = { enum call_status active[] = {
CALL_STATUS_ACTIVE, CALL_STATUS_ACTIVE,
CALL_STATUS_DIALING, CALL_STATUS_DIALING,
CALL_STATUS_ALERTING CALL_STATUS_ALERTING,
CALL_STATUS_INCOMING,
}; };
int i; int i;
DBG(""); DBG("");
for (i = 0; i < ARRAY_SIZE(active); i++) { for (i = 0; i < ARRAY_SIZE(active); i++) {
list = g_slist_find_custom(vd->call_list, list = g_slist_find_custom(vd->call_list,
GINT_TO_POINTER(CALL_STATUS_ACTIVE), GINT_TO_POINTER(active[i]),
ofono_call_compare_by_status); ofono_call_compare_by_status);
if (list) if (list)

View File

@ -61,6 +61,9 @@
/* size of RIL_CellInfoTdscdma */ /* size of RIL_CellInfoTdscdma */
#define NETMON_RIL_CELLINFO_SIZE_TDSCDMA 24 #define NETMON_RIL_CELLINFO_SIZE_TDSCDMA 24
#define MSECS_RATE_INVALID (0x7fffffff)
#define SECS_TO_MSECS(x) ((x) * 1000)
struct netmon_data { struct netmon_data {
GRil *ril; GRil *ril;
}; };
@ -96,11 +99,9 @@ static int ril_cell_type_to_size(int cell_type)
return 0; return 0;
} }
static void ril_netmon_update_cb(struct ril_msg *message, gpointer user_data) static int process_cellinfo_list(struct ril_msg *message,
struct ofono_netmon *netmon)
{ {
struct cb_data *cbd = user_data;
ofono_netmon_cb_t cb = cbd->cb;
struct ofono_netmon *netmon = cbd->data;
struct parcel rilp; struct parcel rilp;
int skip_len; int skip_len;
int cell_info_cnt; int cell_info_cnt;
@ -114,7 +115,7 @@ static void ril_netmon_update_cb(struct ril_msg *message, gpointer user_data)
int i, j; int i, j;
if (message->error != RIL_E_SUCCESS) if (message->error != RIL_E_SUCCESS)
goto error; return OFONO_ERROR_TYPE_FAILURE;
g_ril_init_parcel(message, &rilp); g_ril_init_parcel(message, &rilp);
@ -146,7 +147,7 @@ static void ril_netmon_update_cb(struct ril_msg *message, gpointer user_data)
} }
if (!registered) if (!registered)
goto error; return OFONO_ERROR_TYPE_FAILURE;
if (cell_type == NETMON_RIL_CELLINFO_TYPE_GSM) { if (cell_type == NETMON_RIL_CELLINFO_TYPE_GSM) {
mcc = parcel_r_int32(&rilp); mcc = parcel_r_int32(&rilp);
@ -216,17 +217,53 @@ static void ril_netmon_update_cb(struct ril_msg *message, gpointer user_data)
OFONO_NETMON_INFO_BER, ber, OFONO_NETMON_INFO_BER, ber,
OFONO_NETMON_INFO_INVALID); OFONO_NETMON_INFO_INVALID);
} else {
goto error;
} }
CALLBACK_WITH_SUCCESS(cb, cbd->data); return OFONO_ERROR_TYPE_NO_ERROR;
return; }
static void ril_netmon_update_cb(struct ril_msg *message, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_netmon_cb_t cb = cbd->cb;
struct ofono_netmon *netmon = cbd->data;
if (process_cellinfo_list(message, netmon) ==
OFONO_ERROR_TYPE_NO_ERROR) {
CALLBACK_WITH_SUCCESS(cb, cbd->data);
return;
}
error:
CALLBACK_WITH_FAILURE(cb, cbd->data); CALLBACK_WITH_FAILURE(cb, cbd->data);
} }
static void ril_cellinfo_notify(struct ril_msg *message, gpointer user_data)
{
struct ofono_netmon *netmon = user_data;
process_cellinfo_list(message, netmon);
}
static void setup_cell_info_notify(struct ofono_netmon *netmon)
{
struct netmon_data *nmd = ofono_netmon_get_data(netmon);
struct parcel rilp;
parcel_init(&rilp);
parcel_w_int32(&rilp, 1); /* Number of elements */
parcel_w_int32(&rilp, MSECS_RATE_INVALID);
if (g_ril_send(nmd->ril, RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE,
&rilp, NULL, NULL, NULL) == 0)
ofono_error("%s: setup failed\n", __func__);
if (g_ril_register(nmd->ril, RIL_UNSOL_CELL_INFO_LIST,
ril_cellinfo_notify, netmon) == 0)
ofono_error("%s: setup failed\n", __func__);
}
static int ril_netmon_probe(struct ofono_netmon *netmon, static int ril_netmon_probe(struct ofono_netmon *netmon,
unsigned int vendor, void *user) unsigned int vendor, void *user)
{ {
@ -237,6 +274,8 @@ static int ril_netmon_probe(struct ofono_netmon *netmon,
ofono_netmon_set_data(netmon, ud); ofono_netmon_set_data(netmon, ud);
setup_cell_info_notify(netmon);
g_idle_add(ril_delayed_register, netmon); g_idle_add(ril_delayed_register, netmon);
return 0; return 0;
@ -257,18 +296,55 @@ static void ril_netmon_request_update(struct ofono_netmon *netmon,
struct cb_data *cbd = cb_data_new(cb, data, nmd); struct cb_data *cbd = cb_data_new(cb, data, nmd);
if (g_ril_send(nmd->ril, RIL_REQUEST_GET_CELL_INFO_LIST, NULL, if (g_ril_send(nmd->ril, RIL_REQUEST_GET_CELL_INFO_LIST, NULL,
ril_netmon_update_cb, cbd, NULL) > 0) ril_netmon_update_cb, cbd, g_free) > 0)
return; return;
g_free(cbd); g_free(cbd);
CALLBACK_WITH_FAILURE(cb, data); CALLBACK_WITH_FAILURE(cb, data);
} }
static void periodic_update_cb(struct ril_msg *message, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_netmon_cb_t cb = cbd->cb;
if (message->error != RIL_E_SUCCESS)
CALLBACK_WITH_FAILURE(cb, cbd->data);
CALLBACK_WITH_SUCCESS(cb, cbd->data);
}
static void ril_netmon_periodic_update(struct ofono_netmon *netmon,
unsigned int enable, unsigned int period,
ofono_netmon_cb_t cb, void *data)
{
struct netmon_data *nmd = ofono_netmon_get_data(netmon);
struct cb_data *cbd = cb_data_new(cb, data, nmd);
struct parcel rilp;
parcel_init(&rilp);
parcel_w_int32(&rilp, 1); /* Number of elements */
if (enable)
parcel_w_int32(&rilp, SECS_TO_MSECS(period));
else
parcel_w_int32(&rilp, MSECS_RATE_INVALID);
if (g_ril_send(nmd->ril, RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE,
&rilp, periodic_update_cb, cbd, g_free) > 0)
return;
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, cbd->data);
}
static struct ofono_netmon_driver driver = { static struct ofono_netmon_driver driver = {
.name = RILMODEM, .name = RILMODEM,
.probe = ril_netmon_probe, .probe = ril_netmon_probe,
.remove = ril_netmon_remove, .remove = ril_netmon_remove,
.request_update = ril_netmon_request_update, .request_update = ril_netmon_request_update,
.enable_periodic_update = ril_netmon_periodic_update,
}; };
void ril_netmon_init(void) void ril_netmon_init(void)

View File

@ -0,0 +1,233 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 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 <ofono/log.h>
#include <ofono/modem.h>
#include <ofono/radio-settings.h>
#include "gatchat.h"
#include "gatresult.h"
#include "xmm7modem.h"
static const char *none_prefix[] = { NULL };
static const char *xact_prefix[] = { "+XACT:", NULL };
struct radio_settings_data {
GAtChat *chat;
};
static void xact_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_radio_settings_rat_mode_query_cb_t cb = cbd->cb;
enum ofono_radio_access_mode mode;
struct ofono_error error;
GAtResultIter iter;
int value, preferred;
decode_at_error(&error, g_at_result_final_response(result));
if (!ok) {
cb(&error, -1, cbd->data);
return;
}
g_at_result_iter_init(&iter, result);
if (g_at_result_iter_next(&iter, "+XACT:") == FALSE)
goto error;
if (g_at_result_iter_next_number(&iter, &value) == FALSE)
goto error;
if (g_at_result_iter_next_number(&iter, &preferred) == FALSE)
goto error;
switch (value) {
case 0:
mode = OFONO_RADIO_ACCESS_MODE_GSM;
break;
case 1:
mode = OFONO_RADIO_ACCESS_MODE_UMTS;
break;
case 2:
mode = OFONO_RADIO_ACCESS_MODE_LTE;
break;
case 3:
mode = OFONO_RADIO_ACCESS_MODE_UMTS;
break;
case 4:
mode = OFONO_RADIO_ACCESS_MODE_LTE;
break;
case 5:
mode = OFONO_RADIO_ACCESS_MODE_LTE;
break;
case 6:
mode = OFONO_RADIO_ACCESS_MODE_ANY;
break;
default:
CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
return;
}
cb(&error, mode, cbd->data);
return;
error:
CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
}
static void xmm_query_rat_mode(struct ofono_radio_settings *rs,
ofono_radio_settings_rat_mode_query_cb_t cb,
void *data)
{
struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
struct cb_data *cbd = cb_data_new(cb, data);
if (g_at_chat_send(rsd->chat, "AT+XACT?", xact_prefix,
xact_query_cb, cbd, g_free) > 0)
return;
CALLBACK_WITH_FAILURE(cb, -1, data);
g_free(cbd);
}
static void xact_modify_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_radio_settings_rat_mode_set_cb_t cb = cbd->cb;
struct ofono_error error;
decode_at_error(&error, g_at_result_final_response(result));
cb(&error, cbd->data);
}
static void xmm_set_rat_mode(struct ofono_radio_settings *rs,
enum ofono_radio_access_mode mode,
ofono_radio_settings_rat_mode_set_cb_t cb,
void *data)
{
struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
struct cb_data *cbd = cb_data_new(cb, data);
char buf[20];
int value = 6, preferred = 2;
switch (mode) {
case OFONO_RADIO_ACCESS_MODE_ANY:
value = 6;
break;
case OFONO_RADIO_ACCESS_MODE_GSM:
value = 0;
break;
case OFONO_RADIO_ACCESS_MODE_UMTS:
value = 1;
break;
case OFONO_RADIO_ACCESS_MODE_LTE:
value = 2;
break;
}
if (value == 6)
snprintf(buf, sizeof(buf), "AT+XACT=%u,%u", value, preferred);
else
snprintf(buf, sizeof(buf), "AT+XACT=%u", value);
if (g_at_chat_send(rsd->chat, buf, none_prefix,
xact_modify_cb, cbd, g_free) > 0)
return;
CALLBACK_WITH_FAILURE(cb, data);
g_free(cbd);
}
static void xact_support_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_radio_settings *rs = user_data;
if (!ok) {
ofono_radio_settings_remove(rs);
return;
}
ofono_radio_settings_register(rs);
}
static int xmm_radio_settings_probe(struct ofono_radio_settings *rs,
unsigned int vendor, void *user)
{
GAtChat *chat = user;
struct radio_settings_data *rsd;
rsd = g_try_new0(struct radio_settings_data, 1);
if (rsd == NULL)
return -ENOMEM;
rsd->chat = g_at_chat_clone(chat);
ofono_radio_settings_set_data(rs, rsd);
g_at_chat_send(rsd->chat, "AT+XACT=?", xact_prefix,
xact_support_cb, rs, NULL);
return 0;
}
static void xmm_radio_settings_remove(struct ofono_radio_settings *rs)
{
struct radio_settings_data *rsd = ofono_radio_settings_get_data(rs);
ofono_radio_settings_set_data(rs, NULL);
g_at_chat_unref(rsd->chat);
g_free(rsd);
}
static struct ofono_radio_settings_driver driver = {
.name = "xmm7modem",
.probe = xmm_radio_settings_probe,
.remove = xmm_radio_settings_remove,
.query_rat_mode = xmm_query_rat_mode,
.set_rat_mode = xmm_set_rat_mode
};
void xmm_radio_settings_init(void)
{
ofono_radio_settings_driver_register(&driver);
}
void xmm_radio_settings_exit(void)
{
ofono_radio_settings_driver_unregister(&driver);
}

View File

@ -0,0 +1,50 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 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
#include <glib.h>
#include <gatchat.h>
#define OFONO_API_SUBJECT_TO_CHANGE
#include <ofono/plugin.h>
#include <ofono/types.h>
#include <ofono/modem.h>
#include "xmm7modem.h"
static int xmm7modem_init(void)
{
xmm_radio_settings_init();
return 0;
}
static void xmm7modem_exit(void)
{
xmm_radio_settings_exit();
}
OFONO_PLUGIN_DEFINE(xmm7modem, "Intel xmm7xxx series modem driver",
VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT,
xmm7modem_init, xmm7modem_exit)

View File

@ -0,0 +1,27 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 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
*
*/
#include <drivers/atmodem/atutil.h>
#define XMM7MODEM "xmm7modem"
extern void xmm_radio_settings_init(void);
extern void xmm_radio_settings_exit(void);

View File

@ -338,6 +338,8 @@ const char *ril_request_id_to_string(int req)
return "RIL_REQUEST_GET_CELL_INFO_LIST"; return "RIL_REQUEST_GET_CELL_INFO_LIST";
case RIL_REQUEST_SET_INITIAL_ATTACH_APN: case RIL_REQUEST_SET_INITIAL_ATTACH_APN:
return "RIL_REQUEST_SET_INITIAL_ATTACH_APN"; return "RIL_REQUEST_SET_INITIAL_ATTACH_APN";
case RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE:
return "RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE";
default: default:
return "<INVALID>"; return "<INVALID>";
} }
@ -416,6 +418,8 @@ const char *ril_unsol_request_to_string(int request)
return "UNSOL_EXIT_EMERGENCY_CALLBACK_MODE"; return "UNSOL_EXIT_EMERGENCY_CALLBACK_MODE";
case RIL_UNSOL_RIL_CONNECTED: case RIL_UNSOL_RIL_CONNECTED:
return "UNSOL_RIL_CONNECTED"; return "UNSOL_RIL_CONNECTED";
case RIL_UNSOL_CELL_INFO_LIST:
return "RIL_UNSOL_CELL_INFO_LIST";
default: default:
return "<unknown request>"; return "<unknown request>";
} }

View File

@ -23,6 +23,7 @@
#define __PARCEL_H #define __PARCEL_H
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h>
struct parcel { struct parcel {
char *data; char *data;

View File

@ -348,6 +348,7 @@
#define RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS 107 #define RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS 107
#define RIL_REQUEST_VOICE_RADIO_TECH 108 #define RIL_REQUEST_VOICE_RADIO_TECH 108
#define RIL_REQUEST_GET_CELL_INFO_LIST 109 #define RIL_REQUEST_GET_CELL_INFO_LIST 109
#define RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE 110
#define RIL_REQUEST_SET_INITIAL_ATTACH_APN 111 #define RIL_REQUEST_SET_INITIAL_ATTACH_APN 111
/* RIL Unsolicited Messages */ /* RIL Unsolicited Messages */
@ -388,6 +389,7 @@
#define RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE 1033 #define RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE 1033
#define RIL_UNSOL_RIL_CONNECTED 1034 #define RIL_UNSOL_RIL_CONNECTED 1034
#define RIL_UNSOL_VOICE_RADIO_TECH_CHANGED 1035 #define RIL_UNSOL_VOICE_RADIO_TECH_CHANGED 1035
#define RIL_UNSOL_CELL_INFO_LIST 1036
/* Suplementary services Service class*/ /* Suplementary services Service class*/
#define SERVICE_CLASS_NONE 0 #define SERVICE_CLASS_NONE 0

View File

@ -61,6 +61,7 @@ extern "C" {
#define OFONO_HANDSFREE_INTERFACE OFONO_SERVICE ".Handsfree" #define OFONO_HANDSFREE_INTERFACE OFONO_SERVICE ".Handsfree"
#define OFONO_SIRI_INTERFACE OFONO_SERVICE ".Siri" #define OFONO_SIRI_INTERFACE OFONO_SERVICE ".Siri"
#define OFONO_NETMON_INTERFACE OFONO_SERVICE ".NetworkMonitor" #define OFONO_NETMON_INTERFACE OFONO_SERVICE ".NetworkMonitor"
#define OFONO_NETMON_AGENT_INTERFACE OFONO_SERVICE ".NetworkMonitorAgent"
#define OFONO_LTE_INTERFACE OFONO_SERVICE ".LongTermEvolution" #define OFONO_LTE_INTERFACE OFONO_SERVICE ".LongTermEvolution"
/* CDMA Interfaces */ /* CDMA Interfaces */

View File

@ -29,7 +29,13 @@ extern "C" {
#include <ofono/types.h> #include <ofono/types.h>
struct ofono_gprs_context; struct ofono_gprs_context;
struct ofono_modem;
/*
* ETSI 123.003, Section 9.1:
* the APN has, after encoding as defined in the paragraph below, a maximum
* length of 100 octets
*/
#define OFONO_GPRS_MAX_APN_LENGTH 100 #define OFONO_GPRS_MAX_APN_LENGTH 100
#define OFONO_GPRS_MAX_USERNAME_LENGTH 63 #define OFONO_GPRS_MAX_USERNAME_LENGTH 63
#define OFONO_GPRS_MAX_PASSWORD_LENGTH 255 #define OFONO_GPRS_MAX_PASSWORD_LENGTH 255

View File

@ -39,6 +39,10 @@ struct ofono_netmon_driver {
void (*remove)(struct ofono_netmon *netmon); void (*remove)(struct ofono_netmon *netmon);
void (*request_update)(struct ofono_netmon *netmon, void (*request_update)(struct ofono_netmon *netmon,
ofono_netmon_cb_t cb, void *data); ofono_netmon_cb_t cb, void *data);
void (*enable_periodic_update)(struct ofono_netmon *netmon,
unsigned int enable,
unsigned int period,
ofono_netmon_cb_t cb, void *data);
}; };
enum ofono_netmon_cell_type { enum ofono_netmon_cell_type {

View File

@ -25,6 +25,7 @@
#include <errno.h> #include <errno.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdint.h>
#include <glib.h> #include <glib.h>
#include <gatchat.h> #include <gatchat.h>

View File

@ -252,6 +252,15 @@ error:
shutdown_device(modem); shutdown_device(modem);
} }
static void create_shared_dms(void *user_data)
{
struct ofono_modem *modem = user_data;
struct gobi_data *data = ofono_modem_get_data(modem);
qmi_service_create_shared(data->device, QMI_SERVICE_DMS,
create_dms_cb, modem, NULL);
}
static void discover_cb(uint8_t count, const struct qmi_version *list, static void discover_cb(uint8_t count, const struct qmi_version *list,
void *user_data) void *user_data)
{ {
@ -266,6 +275,13 @@ static void discover_cb(uint8_t count, const struct qmi_version *list,
list[i].type); list[i].type);
switch (list[i].type) { switch (list[i].type) {
case QMI_SERVICE_CONTROL:
/* sync command resets the QMI state */
if (list[i].major > 1 || list[i].minor >= 5)
ofono_modem_set_boolean(modem,
"SupportQMISync",
TRUE);
break;
case QMI_SERVICE_DMS: case QMI_SERVICE_DMS:
data->features |= GOBI_DMS; data->features |= GOBI_DMS;
break; break;
@ -314,18 +330,10 @@ static void discover_cb(uint8_t count, const struct qmi_version *list,
return; return;
} }
qmi_service_create_shared(data->device, QMI_SERVICE_DMS, if (ofono_modem_get_boolean(modem, "SupportQMISync") == TRUE)
create_dms_cb, modem, NULL); qmi_device_sync(data->device, create_shared_dms, modem);
} else
create_shared_dms(modem);
static void sync_cb(void *user_data)
{
struct ofono_modem *modem = user_data;
struct gobi_data *data = ofono_modem_get_data(modem);
DBG("modem in sync");
qmi_device_discover(data->device, discover_cb, modem, NULL);
} }
static int gobi_enable(struct ofono_modem *modem) static int gobi_enable(struct ofono_modem *modem)
@ -355,7 +363,7 @@ static int gobi_enable(struct ofono_modem *modem)
qmi_device_set_close_on_unref(data->device, true); qmi_device_set_close_on_unref(data->device, true);
qmi_device_sync(data->device, sync_cb, modem); qmi_device_discover(data->device, discover_cb, modem, NULL);
return -EINPROGRESS; return -EINPROGRESS;
} }

View File

@ -261,18 +261,34 @@ static gboolean setup_sierra(struct modem_info *modem)
if (g_strcmp0(info->interface, "255/255/255") == 0) { if (g_strcmp0(info->interface, "255/255/255") == 0) {
if (g_strcmp0(info->number, "01") == 0) if (g_strcmp0(info->number, "01") == 0)
diag = info->devnode; diag = info->devnode;
if (g_strcmp0(info->number, "03") == 0) else if (g_strcmp0(info->number, "03") == 0)
mdm = info->devnode; mdm = info->devnode;
else if (g_strcmp0(info->number, "04") == 0) else if (g_strcmp0(info->number, "04") == 0)
app = info->devnode; app = info->devnode;
else if (g_strcmp0(info->number, "07") == 0) else if (g_strcmp0(info->number, "07") == 0)
net = info->devnode; net = info->devnode;
else if (g_strcmp0(info->number, "0a") == 0) { else if (g_strcmp0(info->subsystem, "net") == 0) {
if (g_strcmp0(info->subsystem, "net") == 0) /*
* When using the voice firmware on a mc7304
* the second cdc-wdm interface doesn't handle
* qmi messages properly.
* Some modems still have a working second
* cdc-wdm interface, some are not. But always
* the first interface works.
*/
if (g_strcmp0(info->number, "08") == 0) {
net = info->devnode; net = info->devnode;
else if (g_strcmp0(info->subsystem, } else if (g_strcmp0(info->number, "0a") == 0) {
"usbmisc") == 0) if (net == NULL)
net = info->devnode;
}
} else if (g_strcmp0(info->subsystem, "usbmisc") == 0) {
if (g_strcmp0(info->number, "08") == 0) {
qmi = info->devnode; qmi = info->devnode;
} else if (g_strcmp0(info->number, "0a") == 0) {
if (qmi == NULL)
qmi = info->devnode;
}
} }
} }
} }
@ -860,7 +876,7 @@ static gboolean setup_quectel(struct modem_info *modem)
static gboolean setup_quectelqmi(struct modem_info *modem) static gboolean setup_quectelqmi(struct modem_info *modem)
{ {
const char *qmi = NULL, *net = NULL, *gps = NULL; const char *qmi = NULL, *net = NULL, *gps = NULL, *aux = NULL;
GSList *list; GSList *list;
DBG("%s", modem->syspath); DBG("%s", modem->syspath);
@ -878,8 +894,11 @@ static gboolean setup_quectelqmi(struct modem_info *modem)
else if (g_strcmp0(info->subsystem, "usbmisc") == 0) else if (g_strcmp0(info->subsystem, "usbmisc") == 0)
qmi = info->devnode; qmi = info->devnode;
} else if (g_strcmp0(info->interface, "255/0/0") == 0 && } else if (g_strcmp0(info->interface, "255/0/0") == 0 &&
g_strcmp0(info->number, "02") == 0) { g_strcmp0(info->number, "01") == 0) {
gps = info->devnode; gps = info->devnode;
} else if (g_strcmp0(info->interface, "255/0/0") == 0 &&
g_strcmp0(info->number, "02") == 0) {
aux = info->devnode;
} }
} }
@ -893,8 +912,12 @@ static gboolean setup_quectelqmi(struct modem_info *modem)
ofono_modem_set_string(modem->modem, "Device", qmi); ofono_modem_set_string(modem->modem, "Device", qmi);
ofono_modem_set_string(modem->modem, "NetworkInterface", net); ofono_modem_set_string(modem->modem, "NetworkInterface", net);
DBG("gps=%s aux=%s", gps, aux);
if (gps) if (gps)
ofono_modem_set_string(modem->modem, "GPS", gps); ofono_modem_set_string(modem->modem, "GPS", gps);
if (aux)
ofono_modem_set_string(modem->modem, "Aux", aux);
ofono_modem_set_driver(modem->modem, "gobi"); ofono_modem_set_driver(modem->modem, "gobi");

View File

@ -704,6 +704,9 @@ gboolean is_valid_apn(const char *apn)
int i; int i;
int last_period = 0; int last_period = 0;
if (apn == NULL)
return FALSE;
if (apn[0] == '.' || apn[0] == '\0') if (apn[0] == '.' || apn[0] == '\0')
return FALSE; return FALSE;
@ -742,6 +745,28 @@ void ofono_call_init(struct ofono_call *call)
call->clip_validity = CLIP_VALIDITY_NOT_AVAILABLE; call->clip_validity = CLIP_VALIDITY_NOT_AVAILABLE;
} }
const char *call_status_to_string(enum call_status status)
{
switch (status) {
case CALL_STATUS_ACTIVE:
return "active";
case CALL_STATUS_HELD:
return "held";
case CALL_STATUS_DIALING:
return "dialing";
case CALL_STATUS_ALERTING:
return "alerting";
case CALL_STATUS_INCOMING:
return "incoming";
case CALL_STATUS_WAITING:
return "waiting";
case CALL_STATUS_DISCONNECTED:
return "disconnected";
}
return "unknown";
}
gint ofono_call_compare(gconstpointer a, gconstpointer b) gint ofono_call_compare(gconstpointer a, gconstpointer b)
{ {
const struct ofono_call *ca = a; const struct ofono_call *ca = a;

View File

@ -184,6 +184,7 @@ const char *registration_tech_to_string(int tech);
const char *packet_bearer_to_string(int bearer); const char *packet_bearer_to_string(int bearer);
gboolean is_valid_apn(const char *apn); gboolean is_valid_apn(const char *apn);
const char *call_status_to_string(enum call_status status);
gint ofono_call_compare(gconstpointer a, gconstpointer b); gint ofono_call_compare(gconstpointer a, gconstpointer b);
gint ofono_call_compare_by_status(gconstpointer a, gconstpointer b); gint ofono_call_compare_by_status(gconstpointer a, gconstpointer b);

View File

@ -2596,10 +2596,14 @@ void ofono_gprs_detached_notify(struct ofono_gprs *gprs)
void ofono_gprs_status_notify(struct ofono_gprs *gprs, int status) void ofono_gprs_status_notify(struct ofono_gprs *gprs, int status)
{ {
DBG("%s status %d", __ofono_atom_get_path(gprs->atom), status); DBG("%s status %s (%d)", __ofono_atom_get_path(gprs->atom),
registration_status_to_string(status), status);
gprs->status = status; gprs->status = status;
if (status == 0)
ofono_gprs_detached_notify(gprs);
if (status != NETWORK_REGISTRATION_STATUS_REGISTERED && if (status != NETWORK_REGISTRATION_STATUS_REGISTERED &&
status != NETWORK_REGISTRATION_STATUS_ROAMING) { status != NETWORK_REGISTRATION_STATUS_ROAMING) {
gprs_attached_update(gprs); gprs_attached_update(gprs);

View File

@ -34,6 +34,7 @@
#include <gdbus.h> #include <gdbus.h>
#include "ofono.h" #include "ofono.h"
#include "netmonagent.h"
#define CELL_INFO_DICT_APPEND(p_dict, key, info, type, dbus_type) do { \ #define CELL_INFO_DICT_APPEND(p_dict, key, info, type, dbus_type) do { \
type value; \ type value; \
@ -51,6 +52,7 @@ struct ofono_netmon {
DBusMessage *reply; DBusMessage *reply;
void *driver_data; void *driver_data;
struct ofono_atom *atom; struct ofono_atom *atom;
struct netmon_agent *agent;
}; };
static const char *cell_type_to_tech_name(enum ofono_netmon_cell_type type) static const char *cell_type_to_tech_name(enum ofono_netmon_cell_type type)
@ -72,6 +74,7 @@ void ofono_netmon_serving_cell_notify(struct ofono_netmon *netmon,
int info_type, ...) int info_type, ...)
{ {
va_list arglist; va_list arglist;
DBusMessage *agent_notify = NULL;
DBusMessageIter iter; DBusMessageIter iter;
DBusMessageIter dict; DBusMessageIter dict;
enum ofono_netmon_info next_info_type = info_type; enum ofono_netmon_info next_info_type = info_type;
@ -79,13 +82,18 @@ void ofono_netmon_serving_cell_notify(struct ofono_netmon *netmon,
char *mcc; char *mcc;
char *mnc; char *mnc;
int intval; int intval;
netmon->reply = dbus_message_new_method_return(netmon->pending);
if (netmon->reply == NULL) if (netmon->pending != NULL) {
netmon->reply = dbus_message_new_method_return(netmon->pending);
dbus_message_iter_init_append(netmon->reply, &iter);
} else if (netmon->agent != NULL) {
agent_notify = netmon_agent_new_method_call(netmon->agent,
"ServingCellInformationChanged");
dbus_message_iter_init_append(agent_notify, &iter);
} else
return; return;
dbus_message_iter_init_append(netmon->reply, &iter);
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
OFONO_PROPERTIES_ARRAY_SIGNATURE, OFONO_PROPERTIES_ARRAY_SIGNATURE,
&dict); &dict);
@ -243,6 +251,9 @@ done:
va_end(arglist); va_end(arglist);
dbus_message_iter_close_container(&iter, &dict); dbus_message_iter_close_container(&iter, &dict);
if (agent_notify)
netmon_agent_send_no_reply(netmon->agent, agent_notify);
} }
static void serving_cell_info_callback(const struct ofono_error *error, static void serving_cell_info_callback(const struct ofono_error *error,
@ -291,10 +302,117 @@ static DBusMessage *netmon_get_serving_cell_info(DBusConnection *conn,
return NULL; return NULL;
} }
static void periodic_updates_enabled_cb(const struct ofono_error *error,
void *data)
{
struct ofono_netmon *netmon = data;
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
ofono_error("Error enabling periodic updates");
netmon_agent_free(netmon->agent);
return;
}
}
static void periodic_updates_disabled_cb(const struct ofono_error *error,
void *data)
{
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
ofono_error("Error disabling periodic updates");
}
static void agent_removed_cb(gpointer user_data)
{
struct ofono_netmon *netmon = user_data;
netmon->agent = NULL;
netmon->driver->enable_periodic_update(netmon, 0, 0,
periodic_updates_disabled_cb,
NULL);
}
static DBusMessage *netmon_register_agent(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ofono_netmon *netmon = data;
const char *agent_path;
const unsigned int enable = 1;
unsigned int period;
if (netmon->agent)
return __ofono_error_busy(msg);
if (!netmon->driver->enable_periodic_update)
return __ofono_error_not_implemented(msg);
if (dbus_message_get_args(msg, NULL,
DBUS_TYPE_OBJECT_PATH, &agent_path,
DBUS_TYPE_UINT32, &period,
DBUS_TYPE_INVALID) == FALSE)
return __ofono_error_invalid_args(msg);
if (!__ofono_dbus_valid_object_path(agent_path))
return __ofono_error_invalid_format(msg);
if (!period)
return __ofono_error_invalid_args(msg);
/* minimum period is 5 seconds, to avoid frequent updates*/
if (period < 5)
period = 5;
netmon->agent = netmon_agent_new(agent_path,
dbus_message_get_sender(msg));
if (netmon->agent == NULL)
return __ofono_error_failed(msg);
netmon_agent_set_removed_notify(netmon->agent, agent_removed_cb, netmon);
netmon->driver->enable_periodic_update(netmon, enable, period,
periodic_updates_enabled_cb, netmon);
return dbus_message_new_method_return(msg);
}
static DBusMessage *netmon_unregister_agent(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ofono_netmon *netmon = data;
const char *agent_path;
const char *agent_bus = dbus_message_get_sender(msg);
if (!netmon->driver->enable_periodic_update)
return __ofono_error_not_implemented(msg);
if (dbus_message_get_args(msg, NULL,
DBUS_TYPE_OBJECT_PATH, &agent_path,
DBUS_TYPE_INVALID) == FALSE)
return __ofono_error_invalid_args(msg);
if (netmon->agent == NULL)
return __ofono_error_failed(msg);
if (!netmon_agent_matches(netmon->agent, agent_path, agent_bus))
return __ofono_error_access_denied(msg);
netmon_agent_free(netmon->agent);
return dbus_message_new_method_return(msg);
}
static const GDBusMethodTable netmon_methods[] = { static const GDBusMethodTable netmon_methods[] = {
{ GDBUS_ASYNC_METHOD("GetServingCellInformation", { GDBUS_ASYNC_METHOD("GetServingCellInformation",
NULL, GDBUS_ARGS({ "cellinfo", "a{sv}" }), NULL, GDBUS_ARGS({ "cellinfo", "a{sv}" }),
netmon_get_serving_cell_info) }, netmon_get_serving_cell_info) },
{ GDBUS_METHOD("RegisterAgent",
GDBUS_ARGS({ "path", "o"}, { "period", "u"}), NULL,
netmon_register_agent) },
{ GDBUS_METHOD("UnregisterAgent",
GDBUS_ARGS({ "agent", "o" }), NULL,
netmon_unregister_agent) },
{ } { }
}; };

122
src/netmonagent.c Normal file
View File

@ -0,0 +1,122 @@
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define _GNU_SOURCE
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <glib.h>
#include <gdbus.h>
#include "ofono.h"
#include "netmonagent.h"
struct netmon_agent {
char *path;
char *bus;
guint disconnect_watch;
ofono_destroy_func removed_cb;
void *removed_data;
};
DBusMessage *netmon_agent_new_method_call(struct netmon_agent *agent,
const char *method)
{
DBusMessage *msg = dbus_message_new_method_call(agent->bus,
agent->path,
OFONO_NETMON_AGENT_INTERFACE,
method);
return msg;
}
void netmon_agent_send_no_reply(struct netmon_agent *agent,
DBusMessage *message)
{
DBusConnection *conn = ofono_dbus_get_connection();
dbus_message_set_no_reply(message, TRUE);
g_dbus_send_message(conn, message);
}
static inline void netmon_agent_send_release(struct netmon_agent *agent)
{
DBusMessage *msg = netmon_agent_new_method_call(agent, "Release");
netmon_agent_send_no_reply(agent, msg);
}
ofono_bool_t netmon_agent_matches(struct netmon_agent *agent,
const char *path, const char *sender)
{
return g_str_equal(agent->path, path) &&
g_str_equal(agent->bus, sender);
}
ofono_bool_t netmon_agent_sender_matches(struct netmon_agent *agent,
const char *sender)
{
return g_str_equal(agent->bus, sender);
}
void netmon_agent_set_removed_notify(struct netmon_agent *agent,
ofono_destroy_func destroy,
void *user_data)
{
agent->removed_cb = destroy;
agent->removed_data = user_data;
}
void netmon_agent_free(struct netmon_agent *agent)
{
DBusConnection *conn = ofono_dbus_get_connection();
if (agent == NULL)
return;
if (agent->disconnect_watch) {
netmon_agent_send_release(agent);
g_dbus_remove_watch(conn, agent->disconnect_watch);
agent->disconnect_watch = 0;
}
if (agent->removed_cb)
agent->removed_cb(agent->removed_data);
g_free(agent->path);
g_free(agent->bus);
g_free(agent);
}
static void netmon_agent_disconnect_cb(DBusConnection *conn, void *user_data)
{
struct netmon_agent *agent = user_data;
ofono_debug("Agent exited without calling UnregisterAgent");
agent->disconnect_watch = 0;
netmon_agent_free(agent);
}
struct netmon_agent *netmon_agent_new(const char *path,
const char *sender)
{
struct netmon_agent *agent = g_try_new0(struct netmon_agent, 1);
DBusConnection *conn = ofono_dbus_get_connection();
if (agent == NULL)
return NULL;
agent->bus = g_strdup(sender);
agent->path = g_strdup(path);
agent->disconnect_watch = g_dbus_add_disconnect_watch(conn, sender,
netmon_agent_disconnect_cb,
agent, NULL);
return agent;
}

23
src/netmonagent.h Normal file
View File

@ -0,0 +1,23 @@
struct netmon_agent;
struct netmon_agent *netmon_agent_new(const char *path, const char *sender);
void netmon_agent_free(struct netmon_agent *agent);
void netmon_agent_set_removed_notify(struct netmon_agent *agent,
ofono_destroy_func removed_cb,
void *user_data);
ofono_bool_t netmon_agent_matches(struct netmon_agent *agent,
const char *path, const char *sender);
ofono_bool_t netmon_agent_sender_matches(struct netmon_agent *agent,
const char *sender);
DBusMessage *netmon_agent_new_method_call(struct netmon_agent *netmon,
const char *method);
void netmon_agent_send_no_reply(struct netmon_agent *agent,
DBusMessage *message);
void netmon_agent_test(struct netmon_agent *agent);

View File

@ -1186,6 +1186,9 @@ static void notify_status_watches(struct ofono_netreg *netreg)
const char *mcc = NULL; const char *mcc = NULL;
const char *mnc = NULL; const char *mnc = NULL;
if (netreg->status_watches == NULL)
return;
if (netreg->current_operator) { if (netreg->current_operator) {
mcc = netreg->current_operator->mcc; mcc = netreg->current_operator->mcc;
mnc = netreg->current_operator->mnc; mnc = netreg->current_operator->mnc;

View File

@ -15,6 +15,7 @@
<allow send_interface="org.ofono.SmartMessagingAgent"/> <allow send_interface="org.ofono.SmartMessagingAgent"/>
<allow send_interface="org.ofono.PositioningRequestAgent"/> <allow send_interface="org.ofono.PositioningRequestAgent"/>
<allow send_interface="org.ofono.HandsfreeAudioAgent"/> <allow send_interface="org.ofono.HandsfreeAudioAgent"/>
<allow send_interface="org.ofono.NetworkMonitorAgent"/>
</policy> </policy>
<policy at_console="true"> <policy at_console="true">

View File

@ -167,7 +167,7 @@ static gboolean radio_band_umts_from_string(const char *str,
enum ofono_radio_band_umts *band) enum ofono_radio_band_umts *band)
{ {
if (g_str_equal(str, "any")) { if (g_str_equal(str, "any")) {
*band = OFONO_RADIO_BAND_GSM_ANY; *band = OFONO_RADIO_BAND_UMTS_ANY;
return TRUE; return TRUE;
} else if (g_str_equal(str, "850")) { } else if (g_str_equal(str, "850")) {
*band = OFONO_RADIO_BAND_UMTS_850; *band = OFONO_RADIO_BAND_UMTS_850;

View File

@ -1171,7 +1171,8 @@ static gboolean compute_incoming_msgid(GSList *sms_list,
static void dispatch_app_datagram(struct ofono_sms *sms, static void dispatch_app_datagram(struct ofono_sms *sms,
const struct ofono_uuid *uuid, const struct ofono_uuid *uuid,
int dst, int src, int dst, int src,
unsigned char *buf, unsigned len, const unsigned char *buf,
unsigned int len,
const struct sms_address *addr, const struct sms_address *addr,
const struct sms_scts *scts) const struct sms_scts *scts)
{ {

View File

@ -511,6 +511,20 @@ void ofono_ussd_notify(struct ofono_ussd *ussd, int status, int dcs,
DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID); DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID);
ussd_change_state(ussd, new_state); ussd_change_state(ussd, new_state);
goto free;
} else if (ussd->state == USSD_STATE_USER_ACTION &&
status != OFONO_USSD_STATUS_ACTION_REQUIRED) {
ussd_change_state(ussd, USSD_STATE_IDLE);
if (status == OFONO_USSD_STATUS_NOTIFY && str && str[0]) {
const char *path = __ofono_atom_get_path(ussd->atom);
g_dbus_emit_signal(conn, path,
OFONO_SUPPLEMENTARY_SERVICES_INTERFACE,
"NotificationReceived", DBUS_TYPE_STRING,
&str, DBUS_TYPE_INVALID);
}
goto free; goto free;
} else { } else {
ofono_error("Received an unsolicited USSD but can't handle."); ofono_error("Received an unsolicited USSD but can't handle.");

View File

@ -2252,7 +2252,7 @@ void ofono_voicecall_notify(struct ofono_voicecall *vc,
DBG("Got a voicecall event, status: %s (%d), id: %u, number: %s" DBG("Got a voicecall event, status: %s (%d), id: %u, number: %s"
" called_number: %s, called_name %s", " called_number: %s, called_name %s",
ofono_call_status_to_string(call->status), call_status_to_string(call->status),
call->status, call->id, call->phone_number.number, call->status, call->id, call->phone_number.number,
call->called_number.number, call->name); call->called_number.number, call->name);

View File

@ -0,0 +1,97 @@
#!/usr/bin/python3
from gi.repository import GLib
import sys
import dbus
import dbus.service
import dbus.mainloop.glib
NETMON_INTERFACE = "org.ofono.NetworkMonitor"
AGENT_INTERFACE = "org.ofono.NetworkMonitorAgent"
class NetworkMonitorAgent(dbus.service.Object):
@dbus.service.method(AGENT_INTERFACE,
in_signature="", out_signature="")
def Release(self):
print("Agent Released")
mainloop.quit()
@dbus.service.method(AGENT_INTERFACE,
in_signature="a{sv}", out_signature="")
def ServingCellInformationChanged(self, servingcell):
print("ServingCellInformationChanged notification recieved")
tech = 'Technology'
mcc = 'MobileCountryCode'
mnc = 'MobileNetworkCode'
lac = 'LocationAreaCode'
cid = 'CellId'
psc = 'PrimaryScramblingCode'
rssi = 'Strength'
ber = 'BitErrorRate'
if tech in servingcell:
print(" [ Radio Access Technology = %s]" \
% (servingcell[tech]))
if mcc in servingcell:
print(" [ Mobile Country Code = %s]" \
% (servingcell[mcc]))
if mnc in servingcell:
print(" [ Mobile Network Code = %s]" \
% (servingcell[mnc]))
if lac in servingcell:
print(" [ Location Area Code = %d]" \
% (servingcell[lac]))
if cid in servingcell:
print(" [ Cell Identity = %d]" \
% (servingcell[cid]))
if psc in servingcell:
print(" [ Primary Scrambling Code = %d]" \
% (servingcell[psc]))
if rssi in servingcell:
print(" [ Signal Strength = %d]" \
% (servingcell[rssi]))
if ber in servingcell:
print(" [ Bit Error Rate = %d]" \
% (servingcell[ber]))
print('')
if __name__ == '__main__':
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
if len(sys.argv) < 2:
print("Usage: %s <update_period_in_seconds>" %\
(sys.argv[0]))
sys.exit(1)
bus = dbus.SystemBus()
manager = dbus.Interface(bus.get_object("org.ofono", "/"),
"org.ofono.Manager")
modems = manager.GetModems()
path = modems[0][0]
nm = dbus.Interface(bus.get_object('org.ofono', path),
NETMON_INTERFACE)
path = "/test/netmonagent"
agent = NetworkMonitorAgent(bus, path)
try:
period = int(sys.argv[1])
except:
print("Error: Invalid argument %s" % (sys.argv[1]))
sys.exit(1)
nm.RegisterAgent(path, period)
print("Agent registered")
mainloop = GLib.MainLoop()
mainloop.run()

View File

@ -178,12 +178,12 @@ static void test_ber_tlv_builder_efpnn(void)
ber_tlv_builder_optimize(&builder, NULL, NULL); ber_tlv_builder_optimize(&builder, NULL, NULL);
eons_info = sim_eons_new(1); eons_info = sim_eons_new(1);
sim_eons_add_pnn_record(eons_info, 1, efpnn0, sizeof(efpnn0)); sim_eons_add_pnn_record(eons_info, 1, efpnn0, 8 + 10);
g_assert(!sim_eons_pnn_is_empty(eons_info)); g_assert(!sim_eons_pnn_is_empty(eons_info));
sim_eons_free(eons_info); sim_eons_free(eons_info);
eons_info = sim_eons_new(1); eons_info = sim_eons_new(1);
sim_eons_add_pnn_record(eons_info, 1, efpnn1, sizeof(efpnn1)); sim_eons_add_pnn_record(eons_info, 1, efpnn1, 8 + 6);
g_assert(!sim_eons_pnn_is_empty(eons_info)); g_assert(!sim_eons_pnn_is_empty(eons_info));
sim_eons_free(eons_info); sim_eons_free(eons_info);
} }