From 8ad77d5e10befb0c36699f695cf69719453a43d0 Mon Sep 17 00:00:00 2001 From: John Ernberg Date: Mon, 17 Jul 2017 09:20:36 +0000 Subject: [PATCH 01/60] radio-settings: Fix typo in radio_band_umts_from_string --- src/radio-settings.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/radio-settings.c b/src/radio-settings.c index 4f81a841..7fd92df9 100644 --- a/src/radio-settings.c +++ b/src/radio-settings.c @@ -167,7 +167,7 @@ static gboolean radio_band_umts_from_string(const char *str, enum ofono_radio_band_umts *band) { if (g_str_equal(str, "any")) { - *band = OFONO_RADIO_BAND_GSM_ANY; + *band = OFONO_RADIO_BAND_UMTS_ANY; return TRUE; } else if (g_str_equal(str, "850")) { *band = OFONO_RADIO_BAND_UMTS_850; From 41ad1c1da0098ed0e975265e12659324d5c27df5 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Tue, 25 Jul 2017 15:26:23 +0200 Subject: [PATCH 02/60] voicecall.h: declare struct ofono_modem Fix a warning when only voicecall.h is included: 'struct ofono_modem declared' inside parameter list will not be visible outside of this definition or declaration struct ofono_voicecall *ofono_voicecall_create(struct ofono_modem *modem,... --- include/voicecall.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/voicecall.h b/include/voicecall.h index 4d0b3886..eb897e5e 100644 --- a/include/voicecall.h +++ b/include/voicecall.h @@ -28,6 +28,7 @@ extern "C" { #include +struct ofono_modem; struct ofono_voicecall; typedef void (*ofono_voicecall_cb_t)(const struct ofono_error *error, From a0a95be334d3d779e73079a0af80e6ad95e53119 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Tue, 25 Jul 2017 19:42:09 +0200 Subject: [PATCH 03/60] plugins: Remove AllowedAccessPoints interface Without this patch, a modem that has been removed still shows the AllowedAccessPoints interface --- plugins/allowed-apns.c | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/allowed-apns.c b/plugins/allowed-apns.c index 532d17af..b222b91c 100644 --- a/plugins/allowed-apns.c +++ b/plugins/allowed-apns.c @@ -195,6 +195,7 @@ static void sim_watch(struct ofono_atom *atom, if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) { if (ctx->simwatch_id) { + sim_state_watch(OFONO_SIM_STATE_NOT_PRESENT, data); ofono_sim_remove_state_watch(ctx->sim, ctx->simwatch_id); ctx->simwatch_id = 0; } From 49a7d0aa1b5893027a1100ef2a1d440999208efb Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Tue, 25 Jul 2017 15:17:49 +0200 Subject: [PATCH 04/60] qmimodem: use a default RAT when registering When registering to an operator ofono uses the old RAT. In the case the modem is not connected to any network, this would use QMI_NAS_NETWORK_RAT_NONE which results in the error OP_DEVICE_UNSUPPORTED. Use QMI_NAS_NETWORK_RAT_NO_CHANGE instead to not define any preference. --- drivers/qmimodem/nas.h | 1 + drivers/qmimodem/network-registration.c | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/qmimodem/nas.h b/drivers/qmimodem/nas.h index 09807f83..d2feb464 100644 --- a/drivers/qmimodem/nas.h +++ b/drivers/qmimodem/nas.h @@ -97,6 +97,7 @@ struct qmi_nas_network_rat { } __attribute__((__packed__)) info[0]; } __attribute__((__packed__)); +#define QMI_NAS_NETWORK_RAT_NONE 0x00 #define QMI_NAS_NETWORK_RAT_GSM 0x04 #define QMI_NAS_NETWORK_RAT_UMTS 0x05 #define QMI_NAS_NETWORK_RAT_LTE 0x08 diff --git a/drivers/qmimodem/network-registration.c b/drivers/qmimodem/network-registration.c index 52ad69a6..04de5a5b 100644 --- a/drivers/qmimodem/network-registration.c +++ b/drivers/qmimodem/network-registration.c @@ -356,7 +356,7 @@ static void qmi_register_manual(struct ofono_netreg *netreg, info.mcc = atoi(mcc); info.mnc = atoi(mnc); - info.rat = data->current_rat; + info.rat = QMI_NAS_NETWORK_RAT_NO_CHANGE; qmi_param_append(param, QMI_NAS_PARAM_REGISTER_MANUAL_INFO, sizeof(info), &info); From 980bb705d14c83dc30826d0e1e3f82d1219addc7 Mon Sep 17 00:00:00 2001 From: Slava Monich Date: Wed, 21 Jun 2017 14:54:48 +0300 Subject: [PATCH 05/60] ussd: Switch the state from USER_ACTION to IDLE ... when a USSD notification is received. Some networks send 0 (no further user action required) after the response timeout expires. That should result in the user input form getting removed from the ME screen. --- src/ussd.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/ussd.c b/src/ussd.c index 99fa753f..84f64c67 100644 --- a/src/ussd.c +++ b/src/ussd.c @@ -511,6 +511,20 @@ void ofono_ussd_notify(struct ofono_ussd *ussd, int status, int dcs, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID); 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; } else { ofono_error("Received an unsolicited USSD but can't handle."); From e24ba33915d8fae7cb50cb61c1cfd342bd7e699a Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Tue, 8 Aug 2017 15:53:44 +0200 Subject: [PATCH 06/60] gprs-context: set apn length to 100 bytes According to ETSI TS 123 003 version 9.15.0 Chapter 9.1 --- include/gprs-context.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/gprs-context.h b/include/gprs-context.h index ab673265..7e163cc9 100644 --- a/include/gprs-context.h +++ b/include/gprs-context.h @@ -30,7 +30,7 @@ extern "C" { struct ofono_gprs_context; -#define OFONO_GPRS_MAX_APN_LENGTH 127 +#define OFONO_GPRS_MAX_APN_LENGTH 100 #define OFONO_GPRS_MAX_USERNAME_LENGTH 63 #define OFONO_GPRS_MAX_PASSWORD_LENGTH 255 From eee0765c5d90c2e8e4ea39d06b36edeed9e48e79 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 8 Aug 2017 15:17:18 -0500 Subject: [PATCH 07/60] include: Add a quote from primary source document --- include/gprs-context.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/gprs-context.h b/include/gprs-context.h index 7e163cc9..5f54aa6a 100644 --- a/include/gprs-context.h +++ b/include/gprs-context.h @@ -30,6 +30,11 @@ extern "C" { struct ofono_gprs_context; +/* + * 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_USERNAME_LENGTH 63 #define OFONO_GPRS_MAX_PASSWORD_LENGTH 255 From d9b2dbbd0bb68fc994f4b0e534f25929bf43a66a Mon Sep 17 00:00:00 2001 From: Slava Monich Date: Sat, 19 Aug 2017 12:26:16 +0300 Subject: [PATCH 08/60] sms: Pass const pointer to dispatch_app_datagram --- src/sms.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sms.c b/src/sms.c index 17c5a9c9..b86158e6 100644 --- a/src/sms.c +++ b/src/sms.c @@ -1171,7 +1171,8 @@ static gboolean compute_incoming_msgid(GSList *sms_list, static void dispatch_app_datagram(struct ofono_sms *sms, const struct ofono_uuid *uuid, int dst, int src, - unsigned char *buf, unsigned len, + const unsigned char *buf, + unsigned int len, const struct sms_address *addr, const struct sms_scts *scts) { From fb179957210a5a6c3726ea4b285e88009e6a4cd1 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Tue, 22 Aug 2017 14:25:43 +0200 Subject: [PATCH 09/60] qmi/sms: require WMS version >= 1.2 for bearer calls I've never seen a major 0 service. The gobi2000 comes with WMS 1.0 and doesn't support the bearer command. Guessing it's 1.2 required. --- drivers/qmimodem/sms.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/qmimodem/sms.c b/drivers/qmimodem/sms.c index 91501b75..3f4bdeb2 100644 --- a/drivers/qmimodem/sms.c +++ b/drivers/qmimodem/sms.c @@ -277,7 +277,7 @@ static void qmi_bearer_query(struct ofono_sms *sms, DBG(""); - if (data->major < 1 && data->minor < 2) + if (data->major < 1 || (data->major == 1 && data->minor < 2)) goto error; 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); - if (data->major < 1 && data->minor < 2) + if (data->major < 1 || (data->major == 1 && data->minor < 2)) goto error; domain = bearer_to_domain(bearer); From 8d690efa834a12fb528b820cc8a7c83f5afab5b4 Mon Sep 17 00:00:00 2001 From: Matthijs Kooijman Date: Tue, 22 Aug 2017 18:16:29 +0200 Subject: [PATCH 10/60] at/cdma/ifxmodem: Use /dev/net/tun to check for TUN support Previously, these drivers would check /sys/devices/virtual/misc/tun to see if TUN is supported, and bail out otherwise. However, the tun module can sometimes be autoloaded by opening the /dev/net/tun file. In this case the /dev file already exists, but the /sys file only gets created after the modul is loaded. Additionally, the ppp code does not use the /sys file, but only the /dev file, so checking for the existence of the latter seems a better indicator of expected success. --- drivers/atmodem/gprs-context.c | 4 ++-- drivers/cdmamodem/connman.c | 4 ++-- drivers/ifxmodem/gprs-context.c | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/atmodem/gprs-context.c b/drivers/atmodem/gprs-context.c index 9b9679e8..42ec556d 100644 --- a/drivers/atmodem/gprs-context.c +++ b/drivers/atmodem/gprs-context.c @@ -43,7 +43,7 @@ #include "atmodem.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" @@ -426,7 +426,7 @@ static int at_gprs_context_probe(struct ofono_gprs_context *gc, DBG(""); - if (stat(TUN_SYSFS_DIR, &st) < 0) { + if (stat(TUN_DEV, &st) < 0) { ofono_error("Missing support for TUN/TAP devices"); return -ENODEV; } diff --git a/drivers/cdmamodem/connman.c b/drivers/cdmamodem/connman.c index 8c3265a1..7f0f54b6 100644 --- a/drivers/cdmamodem/connman.c +++ b/drivers/cdmamodem/connman.c @@ -43,7 +43,7 @@ #include "cdmamodem.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" @@ -285,7 +285,7 @@ static int cdma_connman_probe(struct ofono_cdma_connman *cm, DBG(""); - if (stat(TUN_SYSFS_DIR, &st) < 0) { + if (stat(TUN_DEV, &st) < 0) { ofono_error("Missing support for TUN/TAP devices"); return -ENODEV; } diff --git a/drivers/ifxmodem/gprs-context.c b/drivers/ifxmodem/gprs-context.c index b7b102b9..52a3672c 100644 --- a/drivers/ifxmodem/gprs-context.c +++ b/drivers/ifxmodem/gprs-context.c @@ -42,7 +42,7 @@ #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" @@ -470,7 +470,7 @@ static int ifx_gprs_context_probe(struct ofono_gprs_context *gc, DBG(""); - if (stat(TUN_SYSFS_DIR, &st) < 0) { + if (stat(TUN_DEV, &st) < 0) { ofono_error("Missing support for TUN/TAP devices"); return -ENODEV; } From 1cfb17f62c60fdd70230aaaa7a1de651379892e2 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Tue, 22 Aug 2017 23:25:32 -0500 Subject: [PATCH 11/60] AUTHORS: Mention Matthijs' contributions --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 5769bf9c..57629610 100644 --- a/AUTHORS +++ b/AUTHORS @@ -123,3 +123,4 @@ Piotr Haber André Draszik Lukasz Nowak Jonas Bonn +Matthijs Kooijman From 1878f6954d2c221af726c2db4b2c60176958d19a Mon Sep 17 00:00:00 2001 From: Slava Monich Date: Fri, 25 Aug 2017 01:17:31 +0300 Subject: [PATCH 12/60] unit: Avoid use of uninitialised data in test-simutil GTest: run: /testsimutil/ber tlv encode EFpnn ==16777== Conditional jump or move depends on uninitialised value(s) ==16777== at 0x4068CB: ber_tlv_iter_next (simutil.c:369) ==16777== by 0x406C39: ber_tlv_find_by_tag (simutil.c:483) ==16777== by 0x407E1D: sim_eons_add_pnn_record (simutil.c:1027) ==16777== by 0x402C39: test_ber_tlv_builder_efpnn (test-simutil.c:181) ==16777== by 0x4EA3A80: g_test_run_suite_internal ==16777== by 0x4EA3F9A: g_test_run_suite ==16777== by 0x4EA3FD0: g_test_run ==16777== by 0x4042FA: main (test-simutil.c:518) --- unit/test-simutil.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/unit/test-simutil.c b/unit/test-simutil.c index 69dd81ef..490e288f 100644 --- a/unit/test-simutil.c +++ b/unit/test-simutil.c @@ -178,12 +178,12 @@ static void test_ber_tlv_builder_efpnn(void) ber_tlv_builder_optimize(&builder, NULL, NULL); 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)); sim_eons_free(eons_info); 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)); sim_eons_free(eons_info); } From 67b097124c26e8e39d89cd0deebb9cb127a4997c Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Tue, 5 Sep 2017 16:20:30 +0200 Subject: [PATCH 13/60] include/gprs-context.h: declare struct ofono_modem MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix a warning. ./include/gprs-context.h:99:61: error: ‘struct ofono_modem’ declared inside parameter list will not be visible outside of this definition or declaration... --- include/gprs-context.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/gprs-context.h b/include/gprs-context.h index 5f54aa6a..38e5c09d 100644 --- a/include/gprs-context.h +++ b/include/gprs-context.h @@ -29,6 +29,7 @@ extern "C" { #include struct ofono_gprs_context; +struct ofono_modem; /* * ETSI 123.003, Section 9.1: From 7d27ccea5eaea16ce64958999362be64a3b76d12 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Tue, 5 Sep 2017 16:27:21 +0200 Subject: [PATCH 14/60] common: move strlen(apn) check into is_valid_apn() --- src/common.c | 7 +++++++ src/gprs.c | 9 --------- src/lte.c | 3 --- 3 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/common.c b/src/common.c index 17d1d58a..ce07b934 100644 --- a/src/common.c +++ b/src/common.c @@ -31,6 +31,7 @@ #include #include "common.h" +#include "gprs-context.h" #include "util.h" struct error_entry { @@ -702,9 +703,15 @@ gboolean is_valid_apn(const char *apn) int i; int last_period = 0; + if (apn == NULL) + return FALSE; + if (apn[0] == '.' || apn[0] == '\0') return FALSE; + if (strlen(apn) > OFONO_GPRS_MAX_APN_LENGTH) + return FALSE; + for (i = 0; apn[i] != '\0'; i++) { if (g_ascii_isalnum(apn[i])) continue; diff --git a/src/gprs.c b/src/gprs.c index 098ba3d4..a4132cc0 100644 --- a/src/gprs.c +++ b/src/gprs.c @@ -1027,9 +1027,6 @@ static DBusMessage *pri_set_apn(struct pri_context *ctx, DBusConnection *conn, { GKeyFile *settings = ctx->gprs->settings; - if (strlen(apn) > OFONO_GPRS_MAX_APN_LENGTH) - return __ofono_error_invalid_format(msg); - if (g_str_equal(apn, ctx->context.apn)) return dbus_message_new_method_return(msg); @@ -2376,9 +2373,6 @@ static void provision_context(const struct ofono_gprs_provision_data *ap, if (ap->name && strlen(ap->name) > MAX_CONTEXT_NAME_LENGTH) return; - if (ap->apn == NULL || strlen(ap->apn) > OFONO_GPRS_MAX_APN_LENGTH) - return; - if (is_valid_apn(ap->apn) == FALSE) return; @@ -3228,9 +3222,6 @@ static gboolean load_context(struct ofono_gprs *gprs, const char *group) if (apn == NULL) goto error; - if (strlen(apn) > OFONO_GPRS_MAX_APN_LENGTH) - goto error; - if (type == OFONO_GPRS_CONTEXT_TYPE_MMS) { msgproxy = g_key_file_get_string(gprs->settings, group, "MessageProxy", NULL); diff --git a/src/lte.c b/src/lte.c index 70e0c18a..9b20749c 100644 --- a/src/lte.c +++ b/src/lte.c @@ -152,9 +152,6 @@ static DBusMessage *lte_set_default_apn(struct ofono_lte *lte, if (lte->pending) return __ofono_error_busy(msg); - if (strlen(apn) > OFONO_GPRS_MAX_APN_LENGTH) - return __ofono_error_invalid_format(msg); - if (g_str_equal(apn, lte->info.apn)) return dbus_message_new_method_return(msg); From 7df48a5107302215d64a4218cebe58c226130c57 Mon Sep 17 00:00:00 2001 From: Ankit Navik Date: Fri, 1 Sep 2017 11:23:00 +0530 Subject: [PATCH 15/60] atmodem: Add lte atom driver Adds atmodem driver for setting the default APN command. The default APN is manage by config storage. --- Makefile.am | 3 +- drivers/atmodem/atmodem.c | 2 + drivers/atmodem/atmodem.h | 3 + drivers/atmodem/lte.c | 142 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 149 insertions(+), 1 deletion(-) create mode 100644 drivers/atmodem/lte.c diff --git a/Makefile.am b/Makefile.am index 078e3f13..18a806bf 100644 --- a/Makefile.am +++ b/Makefile.am @@ -264,7 +264,8 @@ builtin_sources += drivers/atmodem/atmodem.h \ drivers/atmodem/gprs.c \ drivers/atmodem/gprs-context.c \ drivers/atmodem/sim-auth.c \ - drivers/atmodem/gnss.c + drivers/atmodem/gnss.c \ + drivers/atmodem/lte.c builtin_modules += nwmodem builtin_sources += drivers/atmodem/atutil.h \ diff --git a/drivers/atmodem/atmodem.c b/drivers/atmodem/atmodem.c index 3a55ac2e..684b2282 100644 --- a/drivers/atmodem/atmodem.c +++ b/drivers/atmodem/atmodem.c @@ -52,6 +52,7 @@ static int atmodem_init(void) at_gprs_context_init(); at_sim_auth_init(); at_gnss_init(); + at_lte_init(); return 0; } @@ -76,6 +77,7 @@ static void atmodem_exit(void) at_gprs_exit(); at_gprs_context_exit(); at_gnss_exit(); + at_lte_exit(); } OFONO_PLUGIN_DEFINE(atmodem, "AT modem driver", VERSION, diff --git a/drivers/atmodem/atmodem.h b/drivers/atmodem/atmodem.h index 6be1fe5d..b7370668 100644 --- a/drivers/atmodem/atmodem.h +++ b/drivers/atmodem/atmodem.h @@ -74,3 +74,6 @@ extern void at_sim_auth_exit(void); extern void at_gnss_init(void); extern void at_gnss_exit(void); + +extern void at_lte_init(void); +extern void at_lte_exit(void); diff --git a/drivers/atmodem/lte.c b/drivers/atmodem/lte.c new file mode 100644 index 00000000..61a4cd2b --- /dev/null +++ b/drivers/atmodem/lte.c @@ -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 +#endif + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#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); +} From 2640e0a882c35bc405bf4ffc120903d21dc99fb7 Mon Sep 17 00:00:00 2001 From: Ankit Navik Date: Fri, 1 Sep 2017 11:23:01 +0530 Subject: [PATCH 16/60] Add support for Intel xmm7xxx series modem driver This adds driver as xmm7modem for radio-settings --- Makefile.am | 5 + drivers/xmm7modem/radio-settings.c | 233 +++++++++++++++++++++++++++++ drivers/xmm7modem/xmm7modem.c | 50 +++++++ drivers/xmm7modem/xmm7modem.h | 27 ++++ 4 files changed, 315 insertions(+) create mode 100644 drivers/xmm7modem/radio-settings.c create mode 100644 drivers/xmm7modem/xmm7modem.c create mode 100644 drivers/xmm7modem/xmm7modem.h diff --git a/Makefile.am b/Makefile.am index 18a806bf..658e152b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -396,6 +396,11 @@ builtin_sources += drivers/atmodem/atutil.h \ drivers/gemaltomodem/gemaltomodem.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 builtin_modules += phonesim diff --git a/drivers/xmm7modem/radio-settings.c b/drivers/xmm7modem/radio-settings.c new file mode 100644 index 00000000..f22c388a --- /dev/null +++ b/drivers/xmm7modem/radio-settings.c @@ -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 +#endif + +#define _GNU_SOURCE +#include +#include +#include +#include + +#include + +#include +#include +#include + +#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); +} diff --git a/drivers/xmm7modem/xmm7modem.c b/drivers/xmm7modem/xmm7modem.c new file mode 100644 index 00000000..db1864e0 --- /dev/null +++ b/drivers/xmm7modem/xmm7modem.c @@ -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 +#endif + +#include +#include + +#define OFONO_API_SUBJECT_TO_CHANGE +#include +#include +#include + +#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) diff --git a/drivers/xmm7modem/xmm7modem.h b/drivers/xmm7modem/xmm7modem.h new file mode 100644 index 00000000..44fa3d62 --- /dev/null +++ b/drivers/xmm7modem/xmm7modem.h @@ -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 + +#define XMM7MODEM "xmm7modem" + +extern void xmm_radio_settings_init(void); +extern void xmm_radio_settings_exit(void); From b3e4837641ea8854d1aa79e2d9726cf2f4477aa2 Mon Sep 17 00:00:00 2001 From: Christophe Ronco Date: Wed, 6 Sep 2017 11:42:26 +0200 Subject: [PATCH 17/60] atmodem: correctly report lte bearer for huawei modems --- drivers/atmodem/gprs.c | 22 ++++++++++++++++++++++ drivers/atmodem/network-registration.c | 25 +++++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/drivers/atmodem/gprs.c b/drivers/atmodem/gprs.c index 94c7d058..df37d05f 100644 --- a/drivers/atmodem/gprs.c +++ b/drivers/atmodem/gprs.c @@ -327,6 +327,26 @@ static void huawei_mode_notify(GAtResult *result, gpointer user_data) 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) { 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: g_at_chat_register(gd->chat, "^MODE:", huawei_mode_notify, FALSE, gprs, NULL); + g_at_chat_register(gd->chat, "^HCSQ:", huawei_hcsq_notify, + FALSE, gprs, NULL); break; case OFONO_VENDOR_UBLOX: case OFONO_VENDOR_UBLOX_TOBY_L2: diff --git a/drivers/atmodem/network-registration.c b/drivers/atmodem/network-registration.c index c2df0ce1..a5e2af3d 100644 --- a/drivers/atmodem/network-registration.c +++ b/drivers/atmodem/network-registration.c @@ -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) { 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, 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 */ g_at_chat_register(nd->chat, "^NWTIME:", huawei_nwtime_notify, FALSE, netreg, NULL); From 4ac537394e89924ae1864986abdd8f505934a1a3 Mon Sep 17 00:00:00 2001 From: Jonas Bonn Date: Thu, 7 Sep 2017 14:11:33 +0200 Subject: [PATCH 18/60] qmi: implement RAT selection The QMI radio-settings atom was just a skeleton and did not even implement the mandtory property TechnologyPreference. As such, it probably should never even have been registered for the modem. Nonetheless, this patch puts this mandatory property into place. This is implemented via the 'Set System Selection' method by way of the 'mode' parameter. This seems to best reflect the intention of the Ofono API and works as expected when tested with a Quectel EC21. Some notes: i) There is an alternative function called 'Set Technology Preference' which provides similar functionality. This 'technology preference' is updated automatically when the 'system selection mode' is modified so everything seems to be in order. ii) For the EC21, switching the underlying technology works seamlessly. There are indications, however, that some modems _might_ require a reset before changes take effect; that bridge will need to be crossed if reached. --- drivers/qmimodem/nas.h | 11 +++ drivers/qmimodem/radio-settings.c | 115 +++++++++++++++++++++++++++++- 2 files changed, 125 insertions(+), 1 deletion(-) diff --git a/drivers/qmimodem/nas.h b/drivers/qmimodem/nas.h index d2feb464..c3e7546b 100644 --- a/drivers/qmimodem/nas.h +++ b/drivers/qmimodem/nas.h @@ -35,6 +35,8 @@ #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_SET_SYSTEM_SELECTION_PREF 51 +#define QMI_NAS_GET_SYSTEM_SELECTION_PREF 52 /* Set NAS state report conditions */ #define QMI_NAS_PARAM_REPORT_SIGNAL_STRENGTH 0x10 @@ -164,4 +166,13 @@ struct qmi_nas_home_network { char desc[0]; } __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); diff --git a/drivers/qmimodem/radio-settings.c b/drivers/qmimodem/radio-settings.c index 04106ea3..3a2b0684 100644 --- a/drivers/qmimodem/radio-settings.c +++ b/drivers/qmimodem/radio-settings.c @@ -38,6 +38,118 @@ struct settings_data { 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 create_nas_cb(struct qmi_service *service, void *user_data) { struct ofono_radio_settings *rs = user_data; @@ -78,7 +190,6 @@ static int qmi_radio_settings_probe(struct ofono_radio_settings *rs, create_nas_cb, rs, NULL); return 0; - } static void qmi_radio_settings_remove(struct ofono_radio_settings *rs) @@ -100,6 +211,8 @@ static struct ofono_radio_settings_driver driver = { .name = "qmimodem", .probe = qmi_radio_settings_probe, .remove = qmi_radio_settings_remove, + .set_rat_mode = qmi_set_rat_mode, + .query_rat_mode = qmi_query_rat_mode, }; void qmi_radio_settings_init(void) From 76689b81d406372b12b6b55f4534a132499a1637 Mon Sep 17 00:00:00 2001 From: Jonas Bonn Date: Thu, 7 Sep 2017 14:11:35 +0200 Subject: [PATCH 19/60] qmi: add helper to get int16_t result --- drivers/qmimodem/qmi.c | 21 +++++++++++++++++++++ drivers/qmimodem/qmi.h | 2 ++ 2 files changed, 23 insertions(+) diff --git a/drivers/qmimodem/qmi.c b/drivers/qmimodem/qmi.c index 17c6a6f3..c538cb97 100644 --- a/drivers/qmimodem/qmi.c +++ b/drivers/qmimodem/qmi.c @@ -1720,6 +1720,27 @@ bool qmi_result_get_uint8(struct qmi_result *result, uint8_t type, 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, uint16_t *value) { diff --git a/drivers/qmimodem/qmi.h b/drivers/qmimodem/qmi.h index bfe8e6b5..b4955b40 100644 --- a/drivers/qmimodem/qmi.h +++ b/drivers/qmimodem/qmi.h @@ -130,6 +130,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); bool qmi_result_get_uint8(struct qmi_result *result, uint8_t type, 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, uint16_t *value); bool qmi_result_get_uint32(struct qmi_result *result, uint8_t type, From 0b10110a87907ec84fa8b5b03fcbcc824d92efc5 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Thu, 7 Sep 2017 21:33:28 +0200 Subject: [PATCH 20/60] udevng: use first cdc-wdm interface for sierra qmi Using the voice firmware on a mc7304 the modem stopped accepting qmi messages on the second cdc-wdm interface. --- plugins/udevng.c | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/plugins/udevng.c b/plugins/udevng.c index aa28bcb8..1e716a60 100644 --- a/plugins/udevng.c +++ b/plugins/udevng.c @@ -267,12 +267,28 @@ static gboolean setup_sierra(struct modem_info *modem) app = info->devnode; else if (g_strcmp0(info->number, "07") == 0) net = info->devnode; - else if (g_strcmp0(info->number, "0a") == 0) { - if (g_strcmp0(info->subsystem, "net") == 0) + else 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; - else if (g_strcmp0(info->subsystem, - "usbmisc") == 0) + } else if (g_strcmp0(info->number, "0a") == 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; + } else if (g_strcmp0(info->number, "0a") == 0) { + if (qmi == NULL) + qmi = info->devnode; + } } } } From 8d74986d6ea616884bb390b92092db94040dfe06 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Thu, 7 Sep 2017 22:22:57 +0200 Subject: [PATCH 21/60] qmimodem: add strength (in %) to the debug output --- drivers/qmimodem/network-registration.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/qmimodem/network-registration.c b/drivers/qmimodem/network-registration.c index 04de5a5b..41caa414 100644 --- a/drivers/qmimodem/network-registration.c +++ b/drivers/qmimodem/network-registration.c @@ -450,10 +450,11 @@ static void event_notify(struct qmi_result *result, void *user_data) if (ss) { int strength; - DBG("signal with %d dBm on %d", ss->dbm, ss->rat); - 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); } From b81bdfe4566c79a6dcb9907b59bbd7c27934872e Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Thu, 7 Sep 2017 22:22:58 +0200 Subject: [PATCH 22/60] qmimodem: extract network time from serving system --- drivers/qmimodem/nas.h | 12 ++++++++ drivers/qmimodem/network-registration.c | 37 +++++++++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/drivers/qmimodem/nas.h b/drivers/qmimodem/nas.h index c3e7546b..9f67707e 100644 --- a/drivers/qmimodem/nas.h +++ b/drivers/qmimodem/nas.h @@ -152,6 +152,18 @@ struct qmi_nas_current_plmn { #define QMI_NAS_REGISTRATION_STATE_DENIED 0x03 #define QMI_NAS_REGISTRATION_STATE_UNKNOWN 0x04 +#define QMI_NAS_RESULT_3GGP_DST 0x1b +#define QMI_NAS_RESULT_3GPP_TIME 0x1c +struct qmi_nas_3gpp_time { + uint16_t year; + uint8_t month; + uint8_t day; + uint8_t hour; + uint8_t minute; + uint8_t second; + uint8_t timezone; +} __attribute__((__packed__)); + /* cs_state/ps_state */ #define QMI_NAS_ATTACH_STATE_INVALID 0x00 #define QMI_NAS_ATTACH_STATE_ATTACHED 0x01 diff --git a/drivers/qmimodem/network-registration.c b/drivers/qmimodem/network-registration.c index 41caa414..c88e80bd 100644 --- a/drivers/qmimodem/network-registration.c +++ b/drivers/qmimodem/network-registration.c @@ -23,6 +23,7 @@ #include #endif +#include #include #include #include @@ -43,6 +44,38 @@ struct netreg_data { uint8_t current_rat; }; +static bool extract_ss_info_time( + struct qmi_result *result, + struct ofono_network_time *time) +{ + const struct qmi_nas_3gpp_time *time_3gpp = NULL; + uint8_t dst_3gpp; + bool dst_3gpp_valid; + uint16_t len; + + /* parse 3gpp time & dst */ + dst_3gpp_valid = qmi_result_get_uint8(result, QMI_NAS_RESULT_3GGP_DST, + &dst_3gpp); + + time_3gpp = qmi_result_get(result, QMI_NAS_RESULT_3GPP_TIME, &len); + if (time_3gpp && len == sizeof(struct qmi_nas_3gpp_time) && + dst_3gpp_valid) { + time->year = le16toh(time_3gpp->year); + time->mon = time_3gpp->month; + time->mday = time_3gpp->day; + time->hour = time_3gpp->hour; + time->min = time_3gpp->minute; + time->sec = time_3gpp->second; + time->utcoff = time_3gpp->timezone * 15 * 60; + time->dst = dst_3gpp; + return true; + } + + /* TODO: 3gpp2 */ + + return false; +} + static bool extract_ss_info(struct qmi_result *result, int *status, int *lac, int *cellid, int *tech, struct ofono_network_operator *operator) @@ -124,11 +157,15 @@ static bool extract_ss_info(struct qmi_result *result, int *status, static void ss_info_notify(struct qmi_result *result, void *user_data) { struct ofono_netreg *netreg = user_data; + struct ofono_network_time net_time; struct netreg_data *data = ofono_netreg_get_data(netreg); int status, lac, cellid, tech; DBG(""); + if (extract_ss_info_time(result, &net_time)) + ofono_netreg_time_notify(netreg, &net_time); + if (!extract_ss_info(result, &status, &lac, &cellid, &tech, &data->operator)) return; From d08a6043a35d3343167a683b265ccad3186aaa6e Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Thu, 7 Sep 2017 22:22:59 +0200 Subject: [PATCH 23/60] gprs: use registration_status_to_string in debug messages --- src/gprs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/gprs.c b/src/gprs.c index a4132cc0..420c96f9 100644 --- a/src/gprs.c +++ b/src/gprs.c @@ -2596,7 +2596,8 @@ void ofono_gprs_detached_notify(struct ofono_gprs *gprs) 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; From 4e495ab7f4ef1ee51a5603e9c86afb6b1e478e34 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Thu, 7 Sep 2017 22:23:00 +0200 Subject: [PATCH 24/60] plugins/udevng: use else if instead of if The same variable is checked in two `if's. --- plugins/udevng.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/udevng.c b/plugins/udevng.c index 1e716a60..4a11cd10 100644 --- a/plugins/udevng.c +++ b/plugins/udevng.c @@ -261,7 +261,7 @@ static gboolean setup_sierra(struct modem_info *modem) if (g_strcmp0(info->interface, "255/255/255") == 0) { if (g_strcmp0(info->number, "01") == 0) diag = info->devnode; - if (g_strcmp0(info->number, "03") == 0) + else if (g_strcmp0(info->number, "03") == 0) mdm = info->devnode; else if (g_strcmp0(info->number, "04") == 0) app = info->devnode; From 9b16b2c23cde93ff5424de062545aadd96f1f7c6 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Fri, 8 Sep 2017 00:27:32 +0200 Subject: [PATCH 25/60] voicecall,common: move call_status_to_string() to common call_status_to_string() is useful for debug output. Change signature to contain enum call_status Replace default case to get compiler warning when new enums added --- src/common.c | 22 ++++++++++++++++++++++ src/common.h | 1 + src/voicecall.c | 20 -------------------- 3 files changed, 23 insertions(+), 20 deletions(-) diff --git a/src/common.c b/src/common.c index ce07b934..d44b0018 100644 --- a/src/common.c +++ b/src/common.c @@ -743,3 +743,25 @@ void ofono_call_init(struct ofono_call *call) call->cnap_validity = CNAP_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"; +} diff --git a/src/common.h b/src/common.h index 05f2a851..1b6b01d4 100644 --- a/src/common.h +++ b/src/common.h @@ -184,3 +184,4 @@ const char *registration_tech_to_string(int tech); const char *packet_bearer_to_string(int bearer); gboolean is_valid_apn(const char *apn); +const char *call_status_to_string(enum call_status status); diff --git a/src/voicecall.c b/src/voicecall.c index 6907b502..408c7216 100644 --- a/src/voicecall.c +++ b/src/voicecall.c @@ -174,26 +174,6 @@ static const char *disconnect_reason_to_string(enum ofono_disconnect_reason r) } } -static const char *call_status_to_string(int 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"; - default: - return "disconnected"; - } -} - static const char *phone_and_clip_to_string(const struct ofono_phone_number *n, int clip_validity) { From 8e6ac330a5fca98e1db71b936b9cd2e292585d7a Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Fri, 8 Sep 2017 00:27:33 +0200 Subject: [PATCH 26/60] voicecall: use ofono_call_status_name in DBG messages status names are more readable then integer values. --- src/voicecall.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/voicecall.c b/src/voicecall.c index 408c7216..e5b9f505 100644 --- a/src/voicecall.c +++ b/src/voicecall.c @@ -2250,9 +2250,10 @@ void ofono_voicecall_notify(struct ofono_voicecall *vc, struct voicecall *v = NULL; struct ofono_call *newcall; - DBG("Got a voicecall event, status: %d, id: %u, number: %s" - " called_number: %s, called_name %s", call->status, - call->id, call->phone_number.number, + DBG("Got a voicecall event, status: %s (%d), id: %u, number: %s" + " called_number: %s, called_name %s", + call_status_to_string(call->status), + call->status, call->id, call->phone_number.number, call->called_number.number, call->name); l = g_slist_find_custom(vc->call_list, GUINT_TO_POINTER(call->id), From 054323d4bafa2df7e8ec8bd60bdecdd1c7491c8f Mon Sep 17 00:00:00 2001 From: Jonas Bonn Date: Fri, 8 Sep 2017 04:47:33 +0200 Subject: [PATCH 27/60] qmi: provide AvailableTechnologies in radio-settings This provides the list of available technologies in the radio-settings atom. The list is queried by the DMS Get Capabilities method; ofono takes care of caching the available technologies for us so we don't need to worry about this method being called excessively. --- drivers/qmimodem/radio-settings.c | 78 +++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/drivers/qmimodem/radio-settings.c b/drivers/qmimodem/radio-settings.c index 3a2b0684..aa8e0d5c 100644 --- a/drivers/qmimodem/radio-settings.c +++ b/drivers/qmimodem/radio-settings.c @@ -29,11 +29,13 @@ #include "qmi.h" #include "nas.h" +#include "dms.h" #include "qmimodem.h" struct settings_data { struct qmi_service *nas; + struct qmi_service *dms; uint16_t major; uint16_t minor; }; @@ -150,6 +152,79 @@ static void qmi_set_rat_mode(struct ofono_radio_settings *rs, 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) { struct ofono_radio_settings *rs = user_data; @@ -186,6 +261,8 @@ static int qmi_radio_settings_probe(struct ofono_radio_settings *rs, 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, create_nas_cb, rs, NULL); @@ -213,6 +290,7 @@ static struct ofono_radio_settings_driver driver = { .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) From 0acc567a62a5e351ddcdd6bcd1cec270173ff922 Mon Sep 17 00:00:00 2001 From: Jonas Bonn Date: Fri, 8 Sep 2017 04:40:35 +0200 Subject: [PATCH 28/60] doc: RSSI is also an LTE property --- doc/networkmonitor-api.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/networkmonitor-api.txt b/doc/networkmonitor-api.txt index ddace7e4..8d73af00 100644 --- a/doc/networkmonitor-api.txt +++ b/doc/networkmonitor-api.txt @@ -77,7 +77,7 @@ byte TimingAdvance [optional, gsm] 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 in 27.007, Section 8.5. From 0f2d1278c471568fd92e418c9ece73c86701b692 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Fri, 8 Sep 2017 09:56:32 -0500 Subject: [PATCH 29/60] qmimodem: Fix whitespace issue --- drivers/qmimodem/network-registration.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/qmimodem/network-registration.c b/drivers/qmimodem/network-registration.c index c88e80bd..83c1b459 100644 --- a/drivers/qmimodem/network-registration.c +++ b/drivers/qmimodem/network-registration.c @@ -55,7 +55,7 @@ static bool extract_ss_info_time( /* parse 3gpp time & 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); if (time_3gpp && len == sizeof(struct qmi_nas_3gpp_time) && From 1f5aa70b53936012b4d90440d6ede3cd3f36f664 Mon Sep 17 00:00:00 2001 From: Jonas Bonn Date: Fri, 8 Sep 2017 04:40:39 +0200 Subject: [PATCH 30/60] udev: fix quectelqmi gps interface Using location-reporting requires both the 'aux' and 'gps' interfaces; the GPS interface is interface 1, not 2. --- plugins/udevng.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/plugins/udevng.c b/plugins/udevng.c index 4a11cd10..518eda8e 100644 --- a/plugins/udevng.c +++ b/plugins/udevng.c @@ -876,7 +876,7 @@ static gboolean setup_quectel(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; DBG("%s", modem->syspath); @@ -894,8 +894,11 @@ static gboolean setup_quectelqmi(struct modem_info *modem) else if (g_strcmp0(info->subsystem, "usbmisc") == 0) qmi = info->devnode; } 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; + } else if (g_strcmp0(info->interface, "255/0/0") == 0 && + g_strcmp0(info->number, "02") == 0) { + aux = info->devnode; } } @@ -909,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, "NetworkInterface", net); + DBG("gps=%s aux=%s", gps, aux); + if (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"); From 995eaca5149924804944ccd23d750b238bdff392 Mon Sep 17 00:00:00 2001 From: Clayton Craft Date: Fri, 8 Sep 2017 09:18:20 -0700 Subject: [PATCH 31/60] plugins: Fix compilation on armhf/Musl The following patch is needed to successfully build ofono on armhf with musl (in place of libc) --- plugins/gemalto.c | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/gemalto.c b/plugins/gemalto.c index 011713e6..45ec0cf9 100644 --- a/plugins/gemalto.c +++ b/plugins/gemalto.c @@ -25,6 +25,7 @@ #include #include +#include #include #include From 7ff6df60a74860ac45121ad3c8f0e7591e395f5e Mon Sep 17 00:00:00 2001 From: Clayton Craft Date: Fri, 8 Sep 2017 09:18:20 -0700 Subject: [PATCH 32/60] gril: Fix compilation on armhf/Musl The following patch is needed to successfully build ofono on armhf with musl (in place of libc) --- gril/parcel.h | 1 + 1 file changed, 1 insertion(+) diff --git a/gril/parcel.h b/gril/parcel.h index e5592ae1..46887890 100644 --- a/gril/parcel.h +++ b/gril/parcel.h @@ -23,6 +23,7 @@ #define __PARCEL_H #include +#include struct parcel { char *data; From ebcb19622c59a9608fec3ba41997cf544509671b Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Fri, 8 Sep 2017 12:12:11 -0500 Subject: [PATCH 33/60] AUTHORS: Mention Clayton's contributions --- AUTHORS | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS b/AUTHORS index 57629610..a8292db0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -124,3 +124,4 @@ André Draszik Lukasz Nowak Jonas Bonn Matthijs Kooijman +Clayton Craft From 9e83110092619947142917c0e42ebf8a7319717e Mon Sep 17 00:00:00 2001 From: Nishanth V Date: Thu, 7 Sep 2017 09:53:14 +0530 Subject: [PATCH 34/60] netmon: modified api.txt for network monitor agent added new DBUS methods RegisterAgent and UnregisterAgent to Networkmonitor interface so that any client of ofono can register for serving cell updates. Added new agent interface NetworkMonitorAgent with two methods, ServingCellInformationChanged and Release. --- doc/networkmonitor-api.txt | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/doc/networkmonitor-api.txt b/doc/networkmonitor-api.txt index 8d73af00..e8fae8f7 100644 --- a/doc/networkmonitor-api.txt +++ b/doc/networkmonitor-api.txt @@ -22,6 +22,34 @@ Methods a{sv} GetServingCellInformation() are available, their valid value ranges and 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}) + + 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 ============================== From 2390820d323c5a411b48e64f49edbeca55d4bd27 Mon Sep 17 00:00:00 2001 From: Nishanth V Date: Thu, 7 Sep 2017 09:53:15 +0530 Subject: [PATCH 35/60] include: added enable_periodic_update in netmon driver Defining new method in ofono_netmon_driver, enable_periodic_update useful to get periodic update on serving cell information. --- include/netmon.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/netmon.h b/include/netmon.h index 72090c32..c8fcafa6 100644 --- a/include/netmon.h +++ b/include/netmon.h @@ -39,6 +39,10 @@ struct ofono_netmon_driver { void (*remove)(struct ofono_netmon *netmon); void (*request_update)(struct ofono_netmon *netmon, 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 { From 80dec9b080d7fc601e6b9bb9a42db1cb7d9ab3ec Mon Sep 17 00:00:00 2001 From: Nishanth V Date: Thu, 7 Sep 2017 09:53:16 +0530 Subject: [PATCH 36/60] include: added NetworkMonitorAgent interface --- include/dbus.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/dbus.h b/include/dbus.h index cf68ae5f..a6519c7c 100644 --- a/include/dbus.h +++ b/include/dbus.h @@ -61,6 +61,7 @@ extern "C" { #define OFONO_HANDSFREE_INTERFACE OFONO_SERVICE ".Handsfree" #define OFONO_SIRI_INTERFACE OFONO_SERVICE ".Siri" #define OFONO_NETMON_INTERFACE OFONO_SERVICE ".NetworkMonitor" +#define OFONO_NETMON_AGENT_INTERFACE OFONO_SERVICE ".NetworkMonitorAgent" #define OFONO_LTE_INTERFACE OFONO_SERVICE ".LongTermEvolution" /* CDMA Interfaces */ From bcc37ad77f5d75aabbb91a9fe37c53030d1e47de Mon Sep 17 00:00:00 2001 From: Nishanth V Date: Thu, 7 Sep 2017 09:53:17 +0530 Subject: [PATCH 37/60] netmon: core changes for network monitor agent Added implementation for RegisterAgent and UnregisterAgent in NetworkMonitor interface and added netmonagent source file for agent implemention. --- Makefile.am | 3 +- src/netmon.c | 126 ++++++++++++++++++++++++++++++++++++++++++++-- src/netmonagent.c | 122 ++++++++++++++++++++++++++++++++++++++++++++ src/netmonagent.h | 23 +++++++++ src/ofono.conf | 1 + 5 files changed, 270 insertions(+), 5 deletions(-) create mode 100644 src/netmonagent.c create mode 100644 src/netmonagent.h diff --git a/Makefile.am b/Makefile.am index 658e152b..fbc4d6d5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -626,7 +626,8 @@ src_ofonod_SOURCES = $(builtin_sources) $(gatchat_sources) src/ofono.ver \ src/cdma-provision.c src/handsfree.c \ src/handsfree-audio.c src/bluetooth.h \ src/hfp.h src/siri.c \ - src/netmon.c src/lte.c + src/netmon.c src/lte.c \ + src/netmonagent.c src/netmonagent.h src_ofonod_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \ @GLIB_LIBS@ @DBUS_LIBS@ -ldl diff --git a/src/netmon.c b/src/netmon.c index 64767830..3a495873 100644 --- a/src/netmon.c +++ b/src/netmon.c @@ -34,6 +34,7 @@ #include #include "ofono.h" +#include "netmonagent.h" #define CELL_INFO_DICT_APPEND(p_dict, key, info, type, dbus_type) do { \ type value; \ @@ -51,6 +52,7 @@ struct ofono_netmon { DBusMessage *reply; void *driver_data; struct ofono_atom *atom; + struct netmon_agent *agent; }; 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, ...) { va_list arglist; + DBusMessage *agent_notify = NULL; DBusMessageIter iter; DBusMessageIter dict; 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 *mnc; 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; - dbus_message_iter_init_append(netmon->reply, &iter); - dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, OFONO_PROPERTIES_ARRAY_SIGNATURE, &dict); @@ -243,6 +251,9 @@ done: va_end(arglist); 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, @@ -291,10 +302,117 @@ static DBusMessage *netmon_get_serving_cell_info(DBusConnection *conn, 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[] = { { GDBUS_ASYNC_METHOD("GetServingCellInformation", NULL, GDBUS_ARGS({ "cellinfo", "a{sv}" }), 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) }, { } }; diff --git a/src/netmonagent.c b/src/netmonagent.c new file mode 100644 index 00000000..ab6f050a --- /dev/null +++ b/src/netmonagent.c @@ -0,0 +1,122 @@ +#ifdef HAVE_CONFIG_H +#include +#endif + +#define _GNU_SOURCE +#include +#include +#include + +#include +#include + +#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 excited 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; +} diff --git a/src/netmonagent.h b/src/netmonagent.h new file mode 100644 index 00000000..f64a503a --- /dev/null +++ b/src/netmonagent.h @@ -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); diff --git a/src/ofono.conf b/src/ofono.conf index ed56d3bd..61e0bdec 100644 --- a/src/ofono.conf +++ b/src/ofono.conf @@ -15,6 +15,7 @@ + From 2fde37cdfc398920b072d209b367f616882d0ad3 Mon Sep 17 00:00:00 2001 From: Nishanth V Date: Thu, 7 Sep 2017 09:53:18 +0530 Subject: [PATCH 38/60] gril: added RIL constants for cell info list added RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE and RIL_UNSOL_CELL_INFO_LIST and corresponding string conversion --- gril/grilutil.c | 4 ++++ gril/ril_constants.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/gril/grilutil.c b/gril/grilutil.c index 9e7cd315..89a6d6f7 100644 --- a/gril/grilutil.c +++ b/gril/grilutil.c @@ -338,6 +338,8 @@ const char *ril_request_id_to_string(int req) return "RIL_REQUEST_GET_CELL_INFO_LIST"; case 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: return ""; } @@ -416,6 +418,8 @@ const char *ril_unsol_request_to_string(int request) return "UNSOL_EXIT_EMERGENCY_CALLBACK_MODE"; case RIL_UNSOL_RIL_CONNECTED: return "UNSOL_RIL_CONNECTED"; + case RIL_UNSOL_CELL_INFO_LIST: + return "RIL_UNSOL_CELL_INFO_LIST"; default: return ""; } diff --git a/gril/ril_constants.h b/gril/ril_constants.h index cbc596e0..b707136f 100644 --- a/gril/ril_constants.h +++ b/gril/ril_constants.h @@ -348,6 +348,7 @@ #define RIL_REQUEST_STK_SEND_ENVELOPE_WITH_STATUS 107 #define RIL_REQUEST_VOICE_RADIO_TECH 108 #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 /* RIL Unsolicited Messages */ @@ -388,6 +389,7 @@ #define RIL_UNSOL_EXIT_EMERGENCY_CALLBACK_MODE 1033 #define RIL_UNSOL_RIL_CONNECTED 1034 #define RIL_UNSOL_VOICE_RADIO_TECH_CHANGED 1035 +#define RIL_UNSOL_CELL_INFO_LIST 1036 /* Suplementary services Service class*/ #define SERVICE_CLASS_NONE 0 From 25347b9e839ad4f3c73077878a75b28c312be651 Mon Sep 17 00:00:00 2001 From: Nishanth V Date: Thu, 7 Sep 2017 09:53:19 +0530 Subject: [PATCH 39/60] rilmodem: driver changes for netmon agent Rilmodem driver updated to handle enabling and disabling periodic serving cell updates --- drivers/rilmodem/netmon.c | 98 ++++++++++++++++++++++++++++++++++----- 1 file changed, 87 insertions(+), 11 deletions(-) diff --git a/drivers/rilmodem/netmon.c b/drivers/rilmodem/netmon.c index f7f7c28c..a1e5f6c9 100644 --- a/drivers/rilmodem/netmon.c +++ b/drivers/rilmodem/netmon.c @@ -61,6 +61,9 @@ /* size of RIL_CellInfoTdscdma */ #define NETMON_RIL_CELLINFO_SIZE_TDSCDMA 24 +#define MSECS_RATE_INVALID (0x7fffffff) +#define SECS_TO_MSECS(x) ((x) * 1000) + struct netmon_data { GRil *ril; }; @@ -96,11 +99,9 @@ static int ril_cell_type_to_size(int cell_type) 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; int skip_len; int cell_info_cnt; @@ -114,7 +115,7 @@ static void ril_netmon_update_cb(struct ril_msg *message, gpointer user_data) int i, j; if (message->error != RIL_E_SUCCESS) - goto error; + return OFONO_ERROR_TYPE_FAILURE; 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) - goto error; + return OFONO_ERROR_TYPE_FAILURE; if (cell_type == NETMON_RIL_CELLINFO_TYPE_GSM) { 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_INVALID); - } else { - goto error; } - CALLBACK_WITH_SUCCESS(cb, cbd->data); - return; + return OFONO_ERROR_TYPE_NO_ERROR; +} + +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); } +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, unsigned int vendor, void *user) { @@ -237,6 +274,8 @@ static int ril_netmon_probe(struct ofono_netmon *netmon, ofono_netmon_set_data(netmon, ud); + setup_cell_info_notify(netmon); + g_idle_add(ril_delayed_register, netmon); return 0; @@ -264,11 +303,48 @@ static void ril_netmon_request_update(struct ofono_netmon *netmon, 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 = { .name = RILMODEM, .probe = ril_netmon_probe, .remove = ril_netmon_remove, .request_update = ril_netmon_request_update, + .enable_periodic_update = ril_netmon_periodic_update, }; void ril_netmon_init(void) From 9b023a0b06e4c760d47a3860ff1197ace3c20d96 Mon Sep 17 00:00:00 2001 From: Nishanth V Date: Thu, 7 Sep 2017 09:53:20 +0530 Subject: [PATCH 40/60] test: added script to test serving cell agent --- Makefile.am | 4 +- test/test-serving-cell-info | 97 +++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 test/test-serving-cell-info diff --git a/Makefile.am b/Makefile.am index fbc4d6d5..c46e85a6 100644 --- a/Makefile.am +++ b/Makefile.am @@ -780,7 +780,9 @@ test_scripts = test/backtrace \ test/list-allowed-access-points \ test/enable-throttling \ test/disable-throttling \ - test/set-lte-property + test/set-lte-property \ + test/test-serving-cell-info + if TEST testdir = $(pkglibdir)/test diff --git a/test/test-serving-cell-info b/test/test-serving-cell-info new file mode 100644 index 00000000..498ce88b --- /dev/null +++ b/test/test-serving-cell-info @@ -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 " %\ + (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() From 25db45daaab45dd2d9354b328c419b390cd5ce53 Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Sat, 9 Sep 2017 10:21:20 -0500 Subject: [PATCH 41/60] netmonagent: Fix typo --- src/netmonagent.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/netmonagent.c b/src/netmonagent.c index ab6f050a..dfe1d464 100644 --- a/src/netmonagent.c +++ b/src/netmonagent.c @@ -95,7 +95,7 @@ static void netmon_agent_disconnect_cb(DBusConnection *conn, void *user_data) { struct netmon_agent *agent = user_data; - ofono_debug("Agent excited without calling UnregisterAgent"); + ofono_debug("Agent exited without calling UnregisterAgent"); agent->disconnect_watch = 0; From 9650ab183b1429b9cd7400f16f81816482493e5f Mon Sep 17 00:00:00 2001 From: Denis Kenzior Date: Sat, 9 Sep 2017 10:22:28 -0500 Subject: [PATCH 42/60] doc: Mark ServingCellInformationChanged as noreply --- doc/networkmonitor-api.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/networkmonitor-api.txt b/doc/networkmonitor-api.txt index e8fae8f7..f8cc1c2e 100644 --- a/doc/networkmonitor-api.txt +++ b/doc/networkmonitor-api.txt @@ -38,7 +38,7 @@ Service unique name Interface org.ofono.NetworkMonitorAgent Object path freely definable -Methods void ServingCellInformationChanged(a{sv}) +Methods void ServingCellInformationChanged(a{sv}) [noreply] This method is called whenever the serving cell information has been updated. From cbac15cdd9858e797f223bf9d16116f82e184d88 Mon Sep 17 00:00:00 2001 From: Clayton Craft Date: Sun, 10 Sep 2017 11:41:04 -0700 Subject: [PATCH 43/60] isimodem: Use correct callback for pin status --- drivers/isimodem/sim.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/drivers/isimodem/sim.c b/drivers/isimodem/sim.c index 4379a159..9e86d870 100644 --- a/drivers/isimodem/sim.c +++ b/drivers/isimodem/sim.c @@ -646,8 +646,31 @@ error: /* ISI callback: PIN state (enabled/disabled) query */ static void sec_code_state_resp_cb(const GIsiMessage *msg, void *opaque) { - check_sec_response(msg, opaque, SEC_CODE_STATE_OK_RESP, - SEC_CODE_STATE_FAIL_RESP); + struct isi_cb_data *cbd = opaque; + 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, From 4590c695e043fbbf94736c12cbbea6f853e7c47f Mon Sep 17 00:00:00 2001 From: Nishanth V Date: Mon, 11 Sep 2017 11:45:10 +0530 Subject: [PATCH 44/60] rilmodem: netmon fix missing notify for g_ril_send --- drivers/rilmodem/netmon.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/rilmodem/netmon.c b/drivers/rilmodem/netmon.c index a1e5f6c9..709997c5 100644 --- a/drivers/rilmodem/netmon.c +++ b/drivers/rilmodem/netmon.c @@ -296,7 +296,7 @@ static void ril_netmon_request_update(struct ofono_netmon *netmon, struct cb_data *cbd = cb_data_new(cb, data, nmd); 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; g_free(cbd); From e98f68330944f18202533222fbae9ae4a3c6cbec Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Mon, 11 Sep 2017 02:25:42 +0200 Subject: [PATCH 45/60] network.c: notify_status_watches: check for NULL In rare cases when ofono_netreg_status_notify() is called before ofono_netreg_register() netreg->status_watches is NULL. --- src/network.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/network.c b/src/network.c index a3d41a74..6e69b078 100644 --- a/src/network.c +++ b/src/network.c @@ -1186,6 +1186,9 @@ static void notify_status_watches(struct ofono_netreg *netreg) const char *mcc = NULL; const char *mnc = NULL; + if (netreg->status_watches == NULL) + return; + if (netreg->current_operator) { mcc = netreg->current_operator->mcc; mnc = netreg->current_operator->mnc; From a18c892c6455dddf2c3fdd0001c8a50dcd6629bb Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Mon, 11 Sep 2017 02:25:43 +0200 Subject: [PATCH 46/60] qmimodem: register callbacks after netreg_register When registering callbacks before ofono_netreg_register(), callbacks will use the netreg api which might lead into undefined behaviour, because certain fields aren't yet initilized. --- drivers/qmimodem/network-registration.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/qmimodem/network-registration.c b/drivers/qmimodem/network-registration.c index 83c1b459..c1760b83 100644 --- a/drivers/qmimodem/network-registration.c +++ b/drivers/qmimodem/network-registration.c @@ -511,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) { struct ofono_netreg *netreg = user_data; + struct netreg_data *data = ofono_netreg_get_data(netreg); DBG(""); 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) @@ -536,12 +543,6 @@ static void create_nas_cb(struct qmi_service *service, void *user_data) 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(); if (!param) goto done; From 0982b4c4e80de83c8f281e9c5e3469d93cd67633 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Wed, 12 Jul 2017 21:00:00 +0200 Subject: [PATCH 47/60] common: create GList helper ofono_call_compare replaces at_util_call_compare (atmodem) and call_compare (rild). --- drivers/atmodem/atutil.c | 17 ++--------------- drivers/atmodem/atutil.h | 1 - drivers/atmodem/voicecall.c | 2 +- drivers/hfpmodem/voicecall.c | 2 +- drivers/huaweimodem/voicecall.c | 2 +- drivers/ifxmodem/voicecall.c | 2 +- drivers/rilmodem/voicecall.c | 16 +--------------- drivers/stemodem/voicecall.c | 2 +- src/common.c | 14 ++++++++++++++ src/common.h | 2 ++ 10 files changed, 24 insertions(+), 36 deletions(-) diff --git a/drivers/atmodem/atutil.c b/drivers/atmodem/atutil.c index 14873483..1a48b606 100644 --- a/drivers/atmodem/atutil.c +++ b/drivers/atmodem/atutil.c @@ -31,6 +31,7 @@ #define OFONO_API_SUBJECT_TO_CHANGE #include #include +#include "../src/common.h" #include "atutil.h" #include "vendor.h" @@ -101,20 +102,6 @@ gint at_util_call_compare_by_id(gconstpointer a, gconstpointer b) return 0; } -gint at_util_call_compare(gconstpointer a, gconstpointer b) -{ - const struct ofono_call *ca = a; - const struct ofono_call *cb = b; - - if (ca->id < cb->id) - return -1; - - if (ca->id > cb->id) - return 1; - - return 0; -} - GSList *at_util_parse_clcc(GAtResult *result, unsigned int *ret_mpty_ids) { GAtResultIter iter; @@ -173,7 +160,7 @@ GSList *at_util_parse_clcc(GAtResult *result, unsigned int *ret_mpty_ids) else call->clip_validity = 2; - l = g_slist_insert_sorted(l, call, at_util_call_compare); + l = g_slist_insert_sorted(l, call, ofono_call_compare); if (mpty) mpty_ids |= 1 << id; diff --git a/drivers/atmodem/atutil.h b/drivers/atmodem/atutil.h index 5cb88b78..95e09900 100644 --- a/drivers/atmodem/atutil.h +++ b/drivers/atmodem/atutil.h @@ -54,7 +54,6 @@ void decode_at_error(struct ofono_error *error, const char *final); gint at_util_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_id(gconstpointer a, gconstpointer b); -gint at_util_call_compare(gconstpointer a, gconstpointer b); GSList *at_util_parse_clcc(GAtResult *result, unsigned int *mpty_ids); gboolean at_util_parse_reg(GAtResult *result, const char *prefix, int *mode, int *status, diff --git a/drivers/atmodem/voicecall.c b/drivers/atmodem/voicecall.c index e4c59c26..6fee39bc 100644 --- a/drivers/atmodem/voicecall.c +++ b/drivers/atmodem/voicecall.c @@ -132,7 +132,7 @@ static struct ofono_call *create_call(struct ofono_voicecall *vc, int type, call->clip_validity = clip; call->cnap_validity = CNAP_VALIDITY_NOT_AVAILABLE; - d->calls = g_slist_insert_sorted(d->calls, call, at_util_call_compare); + d->calls = g_slist_insert_sorted(d->calls, call, ofono_call_compare); return call; } diff --git a/drivers/hfpmodem/voicecall.c b/drivers/hfpmodem/voicecall.c index ffdf4b7b..1a282903 100644 --- a/drivers/hfpmodem/voicecall.c +++ b/drivers/hfpmodem/voicecall.c @@ -128,7 +128,7 @@ static struct ofono_call *create_call(struct ofono_voicecall *vc, int type, call->phone_number.type = num_type; } - d->calls = g_slist_insert_sorted(d->calls, call, at_util_call_compare); + d->calls = g_slist_insert_sorted(d->calls, call, ofono_call_compare); call->clip_validity = clip; diff --git a/drivers/huaweimodem/voicecall.c b/drivers/huaweimodem/voicecall.c index f55568d3..0887f652 100644 --- a/drivers/huaweimodem/voicecall.c +++ b/drivers/huaweimodem/voicecall.c @@ -76,7 +76,7 @@ static struct ofono_call *create_call(struct ofono_voicecall *vc, int type, call->clip_validity = clip; - d->calls = g_slist_insert_sorted(d->calls, call, at_util_call_compare); + d->calls = g_slist_insert_sorted(d->calls, call, ofono_call_compare); return call; } diff --git a/drivers/ifxmodem/voicecall.c b/drivers/ifxmodem/voicecall.c index 45b5ca42..581a7906 100644 --- a/drivers/ifxmodem/voicecall.c +++ b/drivers/ifxmodem/voicecall.c @@ -107,7 +107,7 @@ static struct ofono_call *create_call(struct ofono_voicecall *vc, int type, call->clip_validity = clip; - d->calls = g_slist_insert_sorted(d->calls, call, at_util_call_compare); + d->calls = g_slist_insert_sorted(d->calls, call, ofono_call_compare); return call; } diff --git a/drivers/rilmodem/voicecall.c b/drivers/rilmodem/voicecall.c index b7180b90..f248d871 100644 --- a/drivers/rilmodem/voicecall.c +++ b/drivers/rilmodem/voicecall.c @@ -117,20 +117,6 @@ done: ofono_voicecall_disconnected(vc, reqdata->id, reason, NULL); } -static int call_compare(gconstpointer a, gconstpointer b) -{ - const struct ofono_call *ca = a; - const struct ofono_call *cb = b; - - if (ca->id < cb->id) - return -1; - - if (ca->id > cb->id) - return 1; - - return 0; -} - static void clcc_poll_cb(struct ril_msg *message, gpointer user_data) { struct ofono_voicecall *vc = user_data; @@ -209,7 +195,7 @@ static void clcc_poll_cb(struct ril_msg *message, gpointer user_data) call->id, call->status, call->type, call->phone_number.number, call->name); - calls = g_slist_insert_sorted(calls, call, call_compare); + calls = g_slist_insert_sorted(calls, call, ofono_call_compare); } no_calls: diff --git a/drivers/stemodem/voicecall.c b/drivers/stemodem/voicecall.c index 356ab7c6..19276111 100644 --- a/drivers/stemodem/voicecall.c +++ b/drivers/stemodem/voicecall.c @@ -128,7 +128,7 @@ static struct ofono_call *create_call(struct ofono_voicecall *vc, int type, call->clip_validity = clip; - d->calls = g_slist_insert_sorted(d->calls, call, at_util_call_compare); + d->calls = g_slist_insert_sorted(d->calls, call, ofono_call_compare); return call; } diff --git a/src/common.c b/src/common.c index d44b0018..77d28c33 100644 --- a/src/common.c +++ b/src/common.c @@ -765,3 +765,17 @@ const char *call_status_to_string(enum call_status status) return "unknown"; } + +gint ofono_call_compare(gconstpointer a, gconstpointer b) +{ + const struct ofono_call *ca = a; + const struct ofono_call *cb = b; + + if (ca->id < cb->id) + return -1; + + if (ca->id > cb->id) + return 1; + + return 0; +} diff --git a/src/common.h b/src/common.h index 1b6b01d4..ba38d4e0 100644 --- a/src/common.h +++ b/src/common.h @@ -185,3 +185,5 @@ const char *packet_bearer_to_string(int bearer); 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); From 2089bc23c7c2ee5a56accb909140fd9373447ee4 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Tue, 25 Jul 2017 11:34:36 +0200 Subject: [PATCH 48/60] voicecall,common: promote call_status_to_string() public call_status_to_string() is useful for debug output. Change signature to contain enum call_status Replace default case to get compiler warning when new enums added --- src/common.c | 21 +++++++++++++++++++++ src/common.h | 1 + src/voicecall.c | 4 ++-- 3 files changed, 24 insertions(+), 2 deletions(-) diff --git a/src/common.c b/src/common.c index 77d28c33..74b26ad2 100644 --- a/src/common.c +++ b/src/common.c @@ -779,3 +779,24 @@ gint ofono_call_compare(gconstpointer a, gconstpointer b) return 0; } + +const char *ofono_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"; +} diff --git a/src/common.h b/src/common.h index ba38d4e0..003e03cf 100644 --- a/src/common.h +++ b/src/common.h @@ -187,3 +187,4 @@ 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); +const char *ofono_call_status_to_string(enum call_status status); diff --git a/src/voicecall.c b/src/voicecall.c index e5b9f505..d06c2dc2 100644 --- a/src/voicecall.c +++ b/src/voicecall.c @@ -401,7 +401,7 @@ static void append_voicecall_properties(struct voicecall *v, ofono_bool_t mpty; dbus_bool_t emergency_call; - status = call_status_to_string(call->status); + status = ofono_call_status_to_string(call->status); ofono_dbus_dict_append(dict, "State", DBUS_TYPE_STRING, &status); @@ -900,7 +900,7 @@ static void voicecall_set_call_status(struct voicecall *call, int status) call->call->status = status; - status_str = call_status_to_string(status); + status_str = ofono_call_status_to_string(status); path = voicecall_build_path(call->vc, call->call); ofono_dbus_signal_property_changed(conn, path, From 7e3e504d3cdfdc3fac80ef65c88fbb0437bbe3fa Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Tue, 25 Jul 2017 12:42:29 +0200 Subject: [PATCH 49/60] add call-list helper to manage voice call lists Many drivers asks the modem for a complete call list of current calls. These list of calls can be feeded into call-list which parse the list and notify ofono for new calls. --- Makefile.am | 13 ++- include/call-list.h | 38 +++++++ src/call-list.c | 100 +++++++++++++++++++ unit/test-call-list.c | 225 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 373 insertions(+), 3 deletions(-) create mode 100644 include/call-list.h create mode 100644 src/call-list.c create mode 100644 unit/test-call-list.c diff --git a/Makefile.am b/Makefile.am index c46e85a6..50d5ecff 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,7 +22,7 @@ pkginclude_HEADERS = include/log.h include/plugin.h include/history.h \ include/private-network.h include/cdma-netreg.h \ include/cdma-provision.h include/handsfree.h \ include/handsfree-audio.h include/siri.h \ - include/netmon.h include/lte.h + include/netmon.h include/lte.h include/call-list.h nodist_pkginclude_HEADERS = include/version.h @@ -626,7 +626,7 @@ src_ofonod_SOURCES = $(builtin_sources) $(gatchat_sources) src/ofono.ver \ src/cdma-provision.c src/handsfree.c \ src/handsfree-audio.c src/bluetooth.h \ src/hfp.h src/siri.c \ - src/netmon.c src/lte.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) \ @@ -803,7 +803,8 @@ unit_tests = unit/test-common unit/test-util unit/test-idmap \ unit/test-rilmodem-cs \ unit/test-rilmodem-sms \ unit/test-rilmodem-cb \ - unit/test-rilmodem-gprs + unit/test-rilmodem-gprs \ + unit/test-call-list noinst_PROGRAMS = $(unit_tests) \ unit/test-sms-root unit/test-mux unit/test-caif @@ -845,6 +846,12 @@ unit_test_sms_root_SOURCES = unit/test-sms-root.c \ unit_test_sms_root_LDADD = @GLIB_LIBS@ unit_objects += $(unit_test_sms_root_OBJECTS) +unit_test_call_list_SOURCES = \ + src/common.c src/util.c \ + src/call-list.c unit/test-call-list.c +unit_test_call_list_LDADD = @GLIB_LIBS@ -ldl +unit_objects += $(unit_test_call_list_OBJECTS) + unit_test_mux_SOURCES = unit/test-mux.c $(gatchat_sources) unit_test_mux_LDADD = @GLIB_LIBS@ unit_objects += $(unit_test_mux_OBJECTS) diff --git a/include/call-list.h b/include/call-list.h new file mode 100644 index 00000000..dbd6ddd6 --- /dev/null +++ b/include/call-list.h @@ -0,0 +1,38 @@ +/* + * + * oFono - Open Source Telephony + * + * Copyright (C) 2017 Alexander Couzens + * + * 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. + * + */ + +#include + +struct ofono_voicecall; +struct ofono_phone_number; + +/* + * Can be called by the driver in the dialing callback, + * when the new call id already known + */ +void ofono_call_list_dial_callback(struct ofono_voicecall *vc, + GSList **call_list, + const struct ofono_phone_number *ph, + int call_id); + +/* + * Called with a list of known calls e.g. clcc. + * Call list will take ownership of all ofono call within the calls. + */ +void ofono_call_list_notify(struct ofono_voicecall *vc, + GSList **call_list, + GSList *calls); diff --git a/src/call-list.c b/src/call-list.c new file mode 100644 index 00000000..fd39719a --- /dev/null +++ b/src/call-list.c @@ -0,0 +1,100 @@ +/* + * + * oFono - Open Source Telephony + * + * Copyright (C) 2017 Alexander Couzens + * + * 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. + * + */ + +#include + +#include "common.h" + +#include +#include +#include +#include + +#include + +void ofono_call_list_dial_callback(struct ofono_voicecall *vc, + GSList **call_list, + const struct ofono_phone_number *ph, + int call_id) +{ + struct ofono_call *call; + + call = g_new0(struct ofono_call, 1); + call->id = call_id; + + memcpy(&call->called_number, ph, sizeof(*ph)); + call->direction = CALL_DIRECTION_MOBILE_ORIGINATED; + call->status = CALL_STATUS_DIALING; + call->type = 0; /* voice */ + + *call_list = g_slist_insert_sorted(*call_list, + call, + ofono_call_compare); + ofono_voicecall_notify(vc, call); +} + +void ofono_call_list_notify(struct ofono_voicecall *vc, + GSList **call_list, + GSList *calls) +{ + GSList *old_calls = *call_list; + GSList *new_calls = calls; + struct ofono_call *new_call, *old_call; + + while (old_calls || new_calls) { + old_call = old_calls ? old_calls->data : NULL; + new_call = new_calls ? new_calls->data : NULL; + + /* we drop disconnected calls and treat them as not existent */ + if (new_call && new_call->status == CALL_STATUS_DISCONNECTED) { + new_calls = new_calls->next; + calls = g_slist_remove(calls, new_call); + g_free(new_call); + continue; + } + + if (old_call && + (new_call == NULL || + (new_call->id > old_call->id))) { + ofono_voicecall_disconnected( + vc, + old_call->id, + OFONO_DISCONNECT_REASON_UNKNOWN, + NULL); + old_calls = old_calls->next; + } else if (new_call && + (old_call == NULL || + (new_call->id < old_call->id))) { + + /* new call, signal it */ + if (new_call->type == 0) + ofono_voicecall_notify(vc, new_call); + + new_calls = new_calls->next; + } else { + if (memcmp(new_call, old_call, sizeof(*new_call)) + && new_call->type == 0) + ofono_voicecall_notify(vc, new_call); + + new_calls = new_calls->next; + old_calls = old_calls->next; + } + } + + g_slist_free_full(*call_list, g_free); + *call_list = calls; +} diff --git a/unit/test-call-list.c b/unit/test-call-list.c new file mode 100644 index 00000000..980038ab --- /dev/null +++ b/unit/test-call-list.c @@ -0,0 +1,225 @@ +/* + * + * oFono - Open Source Telephony + * + * Copyright (C) 2017 Alexander Couzens + * + * 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. + * + */ + + +#include +#include + + +#include "../src/common.h" +#include +#include + +struct voicecall { +}; + +struct notified { + unsigned int id; + enum call_status status; +}; + +static struct notified notified_list[32]; +static int notified_idx; +static int notified_check; + +void reset_notified(void) +{ + notified_idx = 0; + notified_check = 0; + memset(¬ified_list, 0, sizeof(notified_list)); +} + +void ofono_voicecall_notify(struct ofono_voicecall *vc, + struct ofono_call *call) +{ + notified_list[notified_idx].id = call->id; + notified_list[notified_idx].status = call->status; + notified_idx++; +} + +void ofono_voicecall_disconnected(struct ofono_voicecall *vc, int id, + enum ofono_disconnect_reason reason, + const struct ofono_error *error) +{ + notified_list[notified_idx].id = id; + notified_list[notified_idx].status = CALL_STATUS_DISCONNECTED; + notified_idx++; +} + +static GSList *create_call( + GSList *calls, + unsigned int id, + enum call_status status, + enum call_direction direction) +{ + struct ofono_call *call = g_new0(struct ofono_call, 1); + + call->id = id; + call->status = status; + call->direction = direction; + + calls = g_slist_insert_sorted(calls, call, ofono_call_compare); + + return calls; +} + +static void assert_notified(unsigned int call_id, int call_status) +{ + g_assert(notified_idx >= notified_check); + g_assert(notified_list[notified_check].id == call_id); + g_assert(notified_list[notified_check].status == call_status); + + notified_check++; +} + +static void test_notify_disconnected(void) +{ + struct ofono_voicecall *vc = NULL; + struct ofono_phone_number ph; + GSList *call_list; + GSList *calls; + + strcpy(ph.number, "004888123456"); + ph.type = 0; + + /* reset test */ + reset_notified(); + call_list = NULL; + + /* fill disconnected call*/ + calls = create_call(NULL, 1, CALL_STATUS_DISCONNECTED, + CALL_DIRECTION_MOBILE_TERMINATED); + ofono_call_list_notify(vc, &call_list, calls); + + /* incoming call */ + calls = create_call(NULL, 1, CALL_STATUS_DISCONNECTED, + CALL_DIRECTION_MOBILE_TERMINATED); + calls = create_call(calls, 1, CALL_STATUS_ALERTING, + CALL_DIRECTION_MOBILE_TERMINATED); + ofono_call_list_notify(vc, &call_list, calls); + + /* answer call */ + calls = create_call(NULL, 1, CALL_STATUS_ACTIVE, + CALL_DIRECTION_MOBILE_TERMINATED); + calls = create_call(calls, 1, CALL_STATUS_DISCONNECTED, + CALL_DIRECTION_MOBILE_TERMINATED); + ofono_call_list_notify(vc, &call_list, calls); + + /* another call waiting */ + calls = create_call(NULL, 1, CALL_STATUS_DISCONNECTED, + CALL_DIRECTION_MOBILE_TERMINATED); + calls = create_call(calls, 1, CALL_STATUS_ACTIVE, + CALL_DIRECTION_MOBILE_TERMINATED); + calls = create_call(calls, 2, CALL_STATUS_DISCONNECTED, + CALL_DIRECTION_MOBILE_TERMINATED); + calls = create_call(calls, 2, CALL_STATUS_WAITING, + CALL_DIRECTION_MOBILE_TERMINATED); + calls = create_call(calls, 2, CALL_STATUS_DISCONNECTED, + CALL_DIRECTION_MOBILE_TERMINATED); + ofono_call_list_notify(vc, &call_list, calls); + + /* end all calls */ + ofono_call_list_notify(vc, &call_list, NULL); + + /* verify call history */ + assert_notified(1, CALL_STATUS_ALERTING); + assert_notified(1, CALL_STATUS_ACTIVE); + assert_notified(2, CALL_STATUS_WAITING); + assert_notified(1, CALL_STATUS_DISCONNECTED); + assert_notified(2, CALL_STATUS_DISCONNECTED); + + g_assert(notified_check == notified_idx); + g_slist_free_full(call_list, g_free); +} + +static void test_notify(void) +{ + struct ofono_voicecall *vc = NULL; + struct ofono_phone_number ph; + GSList *call_list; + GSList *calls; + + strcpy(ph.number, "004888123456"); + ph.type = 0; + + /* reset test */ + reset_notified(); + call_list = NULL; + + /* incoming call */ + calls = create_call(NULL, 1, CALL_STATUS_ALERTING, + CALL_DIRECTION_MOBILE_TERMINATED); + ofono_call_list_notify(vc, &call_list, calls); + + /* answer call */ + calls = create_call(NULL, 1, CALL_STATUS_ACTIVE, + CALL_DIRECTION_MOBILE_TERMINATED); + ofono_call_list_notify(vc, &call_list, calls); + + /* another call waiting */ + calls = create_call(NULL, 1, CALL_STATUS_ACTIVE, + CALL_DIRECTION_MOBILE_TERMINATED); + calls = create_call(calls, 2, CALL_STATUS_WAITING, + CALL_DIRECTION_MOBILE_TERMINATED); + ofono_call_list_notify(vc, &call_list, calls); + + /* end all calls */ + ofono_call_list_notify(vc, &call_list, NULL); + + /* verify call history */ + assert_notified(1, CALL_STATUS_ALERTING); + assert_notified(1, CALL_STATUS_ACTIVE); + assert_notified(2, CALL_STATUS_WAITING); + assert_notified(1, CALL_STATUS_DISCONNECTED); + assert_notified(2, CALL_STATUS_DISCONNECTED); + + g_assert(notified_check == notified_idx); + g_slist_free_full(call_list, g_free); +} + +static void test_dial_callback(void) +{ + struct ofono_voicecall *vc = NULL; + struct ofono_phone_number ph; + struct ofono_call *call; + GSList *call_list; + + /* reset test */ + reset_notified(); + call_list = NULL; + + strcpy(ph.number, "0099301234567890"); + ph.type = 0; + + ofono_call_list_dial_callback(vc, &call_list, &ph, 33); + + call = call_list->data; + + g_assert(strcmp(call->called_number.number, ph.number) == 0); + g_slist_free_full(call_list, g_free); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/test-call-list/dial_callback", test_dial_callback); + g_test_add_func("/test-call-list/test_notify", test_notify); + g_test_add_func("/test-call-list/test_notify_disconnected", + test_notify_disconnected); + return g_test_run(); +} From a2e9a50db3c9ce2d5562d5486b6c660d032b04fb Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Tue, 25 Jul 2017 15:35:51 +0200 Subject: [PATCH 50/60] common,atmodem: rename & move at_util_call_compare_by_status to common.c at_util_call_compare_by_status is used by several modem drivers. --- drivers/atmodem/atutil.c | 11 ----------- drivers/atmodem/atutil.h | 2 +- drivers/atmodem/voicecall.c | 16 ++++++++-------- drivers/hfpmodem/voicecall.c | 20 ++++++++++---------- drivers/huaweimodem/voicecall.c | 4 ++-- drivers/ifxmodem/voicecall.c | 10 +++++----- src/common.c | 11 +++++++++++ src/common.h | 1 + 8 files changed, 38 insertions(+), 37 deletions(-) diff --git a/drivers/atmodem/atutil.c b/drivers/atmodem/atutil.c index 1a48b606..fecae19e 100644 --- a/drivers/atmodem/atutil.c +++ b/drivers/atmodem/atutil.c @@ -68,17 +68,6 @@ void decode_at_error(struct ofono_error *error, const char *final) } } -gint at_util_call_compare_by_status(gconstpointer a, gconstpointer b) -{ - const struct ofono_call *call = a; - int status = GPOINTER_TO_INT(b); - - if (status != call->status) - return 1; - - return 0; -} - gint at_util_call_compare_by_phone_number(gconstpointer a, gconstpointer b) { const struct ofono_call *call = a; diff --git a/drivers/atmodem/atutil.h b/drivers/atmodem/atutil.h index 95e09900..4afd21c8 100644 --- a/drivers/atmodem/atutil.h +++ b/drivers/atmodem/atutil.h @@ -51,7 +51,7 @@ enum at_util_charset { typedef void (*at_util_sim_inserted_cb_t)(gboolean present, void *userdata); void decode_at_error(struct ofono_error *error, const char *final); -gint at_util_call_compare_by_status(gconstpointer a, gconstpointer b); +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_id(gconstpointer a, gconstpointer b); GSList *at_util_parse_clcc(GAtResult *result, unsigned int *mpty_ids); diff --git a/drivers/atmodem/voicecall.c b/drivers/atmodem/voicecall.c index 6fee39bc..68cd694c 100644 --- a/drivers/atmodem/voicecall.c +++ b/drivers/atmodem/voicecall.c @@ -660,13 +660,13 @@ static void ring_notify(GAtResult *result, gpointer user_data) /* See comment in CRING */ if (g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_WAITING), - at_util_call_compare_by_status)) + ofono_call_compare_by_status)) return; /* RING can repeat, ignore if we already have an incoming call */ if (g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), - at_util_call_compare_by_status)) + ofono_call_compare_by_status)) return; /* Generate an incoming call of unknown type */ @@ -698,13 +698,13 @@ static void cring_notify(GAtResult *result, gpointer user_data) */ if (g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_WAITING), - at_util_call_compare_by_status)) + ofono_call_compare_by_status)) return; /* CRING can repeat, ignore if we already have an incoming call */ if (g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), - at_util_call_compare_by_status)) + ofono_call_compare_by_status)) return; g_at_result_iter_init(&iter, result); @@ -748,7 +748,7 @@ static void clip_notify(GAtResult *result, gpointer user_data) l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), - at_util_call_compare_by_status); + ofono_call_compare_by_status); if (l == NULL) { ofono_error("CLIP for unknown call"); return; @@ -810,7 +810,7 @@ static void cdip_notify(GAtResult *result, gpointer user_data) l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), - at_util_call_compare_by_status); + ofono_call_compare_by_status); if (l == NULL) { ofono_error("CDIP for unknown call"); return; @@ -859,7 +859,7 @@ static void cnap_notify(GAtResult *result, gpointer user_data) l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), - at_util_call_compare_by_status); + ofono_call_compare_by_status); if (l == NULL) { ofono_error("CNAP for unknown call"); return; @@ -913,7 +913,7 @@ static void ccwa_notify(GAtResult *result, gpointer user_data) /* Some modems resend CCWA, ignore it the second time around */ if (g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_WAITING), - at_util_call_compare_by_status)) + ofono_call_compare_by_status)) return; g_at_result_iter_init(&iter, result); diff --git a/drivers/hfpmodem/voicecall.c b/drivers/hfpmodem/voicecall.c index 1a282903..839b422c 100644 --- a/drivers/hfpmodem/voicecall.c +++ b/drivers/hfpmodem/voicecall.c @@ -85,12 +85,12 @@ static GSList *find_dialing(GSList *calls) GSList *c; c = g_slist_find_custom(calls, GINT_TO_POINTER(CALL_STATUS_DIALING), - at_util_call_compare_by_status); + ofono_call_compare_by_status); if (c == NULL) c = g_slist_find_custom(calls, GINT_TO_POINTER(CALL_STATUS_ALERTING), - at_util_call_compare_by_status); + ofono_call_compare_by_status); return c; } @@ -720,7 +720,7 @@ static void ccwa_notify(GAtResult *result, gpointer user_data) /* CCWA can repeat, ignore if we already have an waiting call */ if (g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_WAITING), - at_util_call_compare_by_status)) + ofono_call_compare_by_status)) return; /* some phones may send extra CCWA after active call is ended @@ -729,7 +729,7 @@ static void ccwa_notify(GAtResult *result, gpointer user_data) */ if (g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), - at_util_call_compare_by_status)) + ofono_call_compare_by_status)) return; @@ -772,7 +772,7 @@ static gboolean clip_timeout(gpointer user_data) l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), - at_util_call_compare_by_status); + ofono_call_compare_by_status); if (l == NULL) return FALSE; @@ -801,12 +801,12 @@ static void ring_notify(GAtResult *result, gpointer user_data) /* RING can repeat, ignore if we already have an incoming call */ if (g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), - at_util_call_compare_by_status)) + ofono_call_compare_by_status)) return; waiting = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_WAITING), - at_util_call_compare_by_status); + ofono_call_compare_by_status); /* If we started receiving RINGS but have a waiting call, most * likely all other calls were dropped and we just didn't get @@ -851,7 +851,7 @@ static void clip_notify(GAtResult *result, gpointer user_data) l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), - at_util_call_compare_by_status); + ofono_call_compare_by_status); if (l == NULL) { ofono_error("CLIP for unknown call"); @@ -967,7 +967,7 @@ static void ciev_callsetup_notify(struct ofono_voicecall *vc, waiting = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_WAITING), - at_util_call_compare_by_status); + ofono_call_compare_by_status); /* This is a truly bizarre case not covered at all by the specification * (yes, they are complete idiots). Here we assume the other side is @@ -1046,7 +1046,7 @@ static void ciev_callsetup_notify(struct ofono_voicecall *vc, { GSList *o = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_DIALING), - at_util_call_compare_by_status); + ofono_call_compare_by_status); if (o) { struct ofono_call *call = o->data; diff --git a/drivers/huaweimodem/voicecall.c b/drivers/huaweimodem/voicecall.c index 0887f652..9be8fb31 100644 --- a/drivers/huaweimodem/voicecall.c +++ b/drivers/huaweimodem/voicecall.c @@ -179,7 +179,7 @@ static void cring_notify(GAtResult *result, gpointer user_data) /* CRING can repeat, ignore if we already have an incoming call */ if (g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), - at_util_call_compare_by_status)) + ofono_call_compare_by_status)) return; g_at_result_iter_init(&iter, result); @@ -218,7 +218,7 @@ static void clip_notify(GAtResult *result, gpointer user_data) l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), - at_util_call_compare_by_status); + ofono_call_compare_by_status); if (l == NULL) { ofono_error("CLIP for unknown call"); return; diff --git a/drivers/ifxmodem/voicecall.c b/drivers/ifxmodem/voicecall.c index 581a7906..60278d00 100644 --- a/drivers/ifxmodem/voicecall.c +++ b/drivers/ifxmodem/voicecall.c @@ -545,12 +545,12 @@ static void cring_notify(GAtResult *result, gpointer user_data) */ if (g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_WAITING), - at_util_call_compare_by_status)) + ofono_call_compare_by_status)) return; l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), - at_util_call_compare_by_status); + ofono_call_compare_by_status); if (l == NULL) { ofono_error("CRING received before XCALLSTAT!!!"); return; @@ -589,7 +589,7 @@ static void clip_notify(GAtResult *result, gpointer user_data) l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), - at_util_call_compare_by_status); + ofono_call_compare_by_status); if (l == NULL) { ofono_error("CLIP for unknown call"); return; @@ -649,7 +649,7 @@ static void cnap_notify(GAtResult *result, gpointer user_data) */ l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_INCOMING), - at_util_call_compare_by_status); + ofono_call_compare_by_status); if (l == NULL) { ofono_error("CNAP for unknown call"); return; @@ -695,7 +695,7 @@ static void ccwa_notify(GAtResult *result, gpointer user_data) l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(CALL_STATUS_WAITING), - at_util_call_compare_by_status); + ofono_call_compare_by_status); if (l == NULL) { ofono_error("CCWA received before XCALLSTAT!!!"); return; diff --git a/src/common.c b/src/common.c index 74b26ad2..1d8d92f0 100644 --- a/src/common.c +++ b/src/common.c @@ -780,6 +780,17 @@ gint ofono_call_compare(gconstpointer a, gconstpointer b) return 0; } +gint ofono_call_compare_by_status(gconstpointer a, gconstpointer b) +{ + const struct ofono_call *call = a; + int status = GPOINTER_TO_INT(b); + + if (status != call->status) + return 1; + + return 0; +} + const char *ofono_call_status_to_string(enum call_status status) { switch (status) { diff --git a/src/common.h b/src/common.h index 003e03cf..630af8b8 100644 --- a/src/common.h +++ b/src/common.h @@ -187,4 +187,5 @@ 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_by_status(gconstpointer a, gconstpointer b); const char *ofono_call_status_to_string(enum call_status status); From 12983c0464442f35ed07e9e2b6c1fc07d0b25486 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Tue, 25 Jul 2017 15:39:36 +0200 Subject: [PATCH 51/60] common,atmodem: rename & move at_util_call_compare_by_id to common.c at_util_call_compare_by_id is used by several modem drivers. --- drivers/atmodem/atutil.c | 14 -------------- drivers/atmodem/atutil.h | 2 +- drivers/huaweimodem/voicecall.c | 6 +++--- drivers/ifxmodem/voicecall.c | 4 ++-- drivers/stemodem/voicecall.c | 2 +- src/common.c | 14 ++++++++++++++ src/common.h | 1 + 7 files changed, 22 insertions(+), 21 deletions(-) diff --git a/drivers/atmodem/atutil.c b/drivers/atmodem/atutil.c index fecae19e..9bf76ed0 100644 --- a/drivers/atmodem/atutil.c +++ b/drivers/atmodem/atutil.c @@ -77,20 +77,6 @@ gint at_util_call_compare_by_phone_number(gconstpointer a, gconstpointer b) sizeof(struct ofono_phone_number)); } -gint at_util_call_compare_by_id(gconstpointer a, gconstpointer b) -{ - const struct ofono_call *call = a; - unsigned int id = GPOINTER_TO_UINT(b); - - if (id < call->id) - return -1; - - if (id > call->id) - return 1; - - return 0; -} - GSList *at_util_parse_clcc(GAtResult *result, unsigned int *ret_mpty_ids) { GAtResultIter iter; diff --git a/drivers/atmodem/atutil.h b/drivers/atmodem/atutil.h index 4afd21c8..862a0a14 100644 --- a/drivers/atmodem/atutil.h +++ b/drivers/atmodem/atutil.h @@ -53,7 +53,7 @@ typedef void (*at_util_sim_inserted_cb_t)(gboolean present, void *userdata); 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_id(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); gboolean at_util_parse_reg(GAtResult *result, const char *prefix, int *mode, int *status, diff --git a/drivers/huaweimodem/voicecall.c b/drivers/huaweimodem/voicecall.c index 9be8fb31..a4a48025 100644 --- a/drivers/huaweimodem/voicecall.c +++ b/drivers/huaweimodem/voicecall.c @@ -347,7 +347,7 @@ static void conf_notify(GAtResult *result, gpointer user_data) ofono_info("Call setup: id %d", call_id); l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(call_id), - at_util_call_compare_by_id); + ofono_call_compare_by_id); if (l == NULL) { ofono_error("Received CONF for untracked call"); return; @@ -384,7 +384,7 @@ static void conn_notify(GAtResult *result, gpointer user_data) ofono_info("Call connect: id %d type %d", call_id, call_type); l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(call_id), - at_util_call_compare_by_id); + ofono_call_compare_by_id); if (l == NULL) { ofono_error("Received CONN for untracked call"); return; @@ -428,7 +428,7 @@ static void cend_notify(GAtResult *result, gpointer user_data) call_id, duration, end_status); l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(call_id), - at_util_call_compare_by_id); + ofono_call_compare_by_id); if (l == NULL) { ofono_error("Received CEND for untracked call"); return; diff --git a/drivers/ifxmodem/voicecall.c b/drivers/ifxmodem/voicecall.c index 60278d00..c02d27e8 100644 --- a/drivers/ifxmodem/voicecall.c +++ b/drivers/ifxmodem/voicecall.c @@ -135,7 +135,7 @@ static void xcallstat_notify(GAtResult *result, gpointer user_data) return; l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(id), - at_util_call_compare_by_id); + ofono_call_compare_by_id); if (l == NULL && status != CALL_STATUS_DIALING && status != CALL_STATUS_INCOMING && @@ -773,7 +773,7 @@ static void xcolp_notify(GAtResult *result, gpointer user_data) l = g_slist_find_custom(vd->calls, GINT_TO_POINTER(call_id), - at_util_call_compare_by_id); + ofono_call_compare_by_id); if (l == NULL) { ofono_error("XCOLP for unknown call"); return; diff --git a/drivers/stemodem/voicecall.c b/drivers/stemodem/voicecall.c index 19276111..849a9f87 100644 --- a/drivers/stemodem/voicecall.c +++ b/drivers/stemodem/voicecall.c @@ -462,7 +462,7 @@ static void ecav_notify(GAtResult *result, gpointer user_data) * If it doesn't exists we make a new one */ l = g_slist_find_custom(vd->calls, GUINT_TO_POINTER(id), - at_util_call_compare_by_id); + ofono_call_compare_by_id); if (l) existing_call = l->data; diff --git a/src/common.c b/src/common.c index 1d8d92f0..cb74318f 100644 --- a/src/common.c +++ b/src/common.c @@ -791,6 +791,20 @@ gint ofono_call_compare_by_status(gconstpointer a, gconstpointer b) return 0; } +gint ofono_call_compare_by_id(gconstpointer a, gconstpointer b) +{ + const struct ofono_call *call = a; + unsigned int id = GPOINTER_TO_UINT(b); + + if (id < call->id) + return -1; + + if (id > call->id) + return 1; + + return 0; +} + const char *ofono_call_status_to_string(enum call_status status) { switch (status) { diff --git a/src/common.h b/src/common.h index 630af8b8..7a2e9263 100644 --- a/src/common.h +++ b/src/common.h @@ -188,4 +188,5 @@ const char *call_status_to_string(enum call_status status); gint ofono_call_compare(gconstpointer a, gconstpointer b); gint ofono_call_compare_by_status(gconstpointer a, gconstpointer b); +gint ofono_call_compare_by_id(gconstpointer a, gconstpointer b); const char *ofono_call_status_to_string(enum call_status status); From 95693632ed6f42b3538e3c49aee6d617a09cae17 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Wed, 26 Jul 2017 02:10:37 +0200 Subject: [PATCH 52/60] NOT_FOR_MERGE: decode qmi messages with libqmi --- Makefile.am | 7 ++++--- drivers/qmimodem/qmi.c | 4 ++++ drivers/qmimodem/qmibridge.c | 40 ++++++++++++++++++++++++++++++++++++ drivers/qmimodem/qmibridge.h | 3 +++ 4 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 drivers/qmimodem/qmibridge.c create mode 100644 drivers/qmimodem/qmibridge.h diff --git a/Makefile.am b/Makefile.am index 50d5ecff..36101372 100644 --- a/Makefile.am +++ b/Makefile.am @@ -233,7 +233,8 @@ builtin_sources += $(qmi_sources) \ drivers/qmimodem/gprs.c \ drivers/qmimodem/gprs-context.c \ drivers/qmimodem/radio-settings.c \ - drivers/qmimodem/location-reporting.c + drivers/qmimodem/location-reporting.c \ + drivers/qmimodem/qmibridge.c builtin_modules += gobi builtin_sources += plugins/gobi.c @@ -630,7 +631,7 @@ src_ofonod_SOURCES = $(builtin_sources) $(gatchat_sources) src/ofono.ver \ src/netmonagent.c src/netmonagent.h 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 \ -Wl,--version-script=$(srcdir)/src/ofono.ver @@ -653,7 +654,7 @@ AM_CFLAGS = @DBUS_CFLAGS@ @GLIB_CFLAGS@ $(builtin_cflags) \ AM_CPPFLAGS = -I$(builddir)/include -I$(builddir)/src -I$(srcdir)/src \ -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/manager-api.txt doc/modem-api.txt doc/network-api.txt \ diff --git a/drivers/qmimodem/qmi.c b/drivers/qmimodem/qmi.c index c538cb97..d682775a 100644 --- a/drivers/qmimodem/qmi.c +++ b/drivers/qmimodem/qmi.c @@ -37,6 +37,7 @@ #include +#include "qmibridge.h" #include "qmi.h" #include "ctl.h" @@ -651,6 +652,8 @@ static gboolean can_write_data(GIOChannel *channel, GIOCondition cond, __debug_msg(' ', req->buf, bytes_written, device->debug_func, device->debug_data); + qmibridge_decode_req(req->buf, req->len); + hdr = req->buf; if (hdr->service == QMI_SERVICE_CONTROL) @@ -838,6 +841,7 @@ static gboolean received_data(GIOChannel *channel, GIOCondition cond, __hexdump('<', buf, bytes_read, device->debug_func, device->debug_data); + qmibridge_decode_read(buf, bytes_read); offset = 0; while (offset < bytes_read) { diff --git a/drivers/qmimodem/qmibridge.c b/drivers/qmimodem/qmibridge.c new file mode 100644 index 00000000..914a1b9a --- /dev/null +++ b/drivers/qmimodem/qmibridge.c @@ -0,0 +1,40 @@ + +#include + +#include +#include + +#include + +#include + +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); +} diff --git a/drivers/qmimodem/qmibridge.h b/drivers/qmimodem/qmibridge.h new file mode 100644 index 00000000..96f8adc5 --- /dev/null +++ b/drivers/qmimodem/qmibridge.h @@ -0,0 +1,3 @@ + +void qmibridge_decode_read(void *data, size_t len); +void qmibridge_decode_req(void *data, size_t len); From b8c75e11e10807f59da08f9018ecf5bb875d9d70 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Wed, 26 Jul 2017 02:11:23 +0200 Subject: [PATCH 53/60] network/ofono_netreg_status_notify: debug output lac and ci The location are code and cell id is updated at the same time. --- src/network.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/network.c b/src/network.c index 6e69b078..d43e598a 100644 --- a/src/network.c +++ b/src/network.c @@ -1351,8 +1351,8 @@ void ofono_netreg_status_notify(struct ofono_netreg *netreg, int status, if (netreg == NULL) return; - DBG("%s status %d tech %d", __ofono_atom_get_path(netreg->atom), - status, tech); + DBG("%s status %d tech %d lac %d ci %d", + __ofono_atom_get_path(netreg->atom), status, tech, lac, ci); if (netreg->status != status) { struct ofono_modem *modem; From cac718f045e7792151ebb29e238fcbb54b56c174 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Wed, 26 Jul 2017 02:12:16 +0200 Subject: [PATCH 54/60] network: debug output the network time if updated --- src/network.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/network.c b/src/network.c index d43e598a..9b5b3645 100644 --- a/src/network.c +++ b/src/network.c @@ -1407,6 +1407,11 @@ void ofono_netreg_time_notify(struct ofono_netreg *netreg, if (info == NULL) return; + DBG("net time %d-%02d-%02d %02d:%02d:%02d utcoff %d dst %d", + info->year, info->mon, info->mday, + info->hour, info->min, info->sec, + info->utcoff, info->dst); + __ofono_nettime_info_received(modem, info); } From ca4ad7e6b0f396cd2c43cb5977588f6000615ecf Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Tue, 15 Aug 2017 07:37:33 +0200 Subject: [PATCH 55/60] atutil.h: remove doublicated ofono_call_compare_by_status() --- drivers/atmodem/atutil.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/atmodem/atutil.h b/drivers/atmodem/atutil.h index 862a0a14..90c82549 100644 --- a/drivers/atmodem/atutil.h +++ b/drivers/atmodem/atutil.h @@ -51,9 +51,7 @@ enum at_util_charset { typedef void (*at_util_sim_inserted_cb_t)(gboolean present, void *userdata); 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 ofono_call_compare_by_id(gconstpointer a, gconstpointer b); GSList *at_util_parse_clcc(GAtResult *result, unsigned int *mpty_ids); gboolean at_util_parse_reg(GAtResult *result, const char *prefix, int *mode, int *status, From 1b26e1e8f81e70c97311e96c927100465e4b7df7 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Tue, 25 Jul 2017 15:31:48 +0200 Subject: [PATCH 56/60] [RFC] qmimodem: implement voice calls The voice_generated.* files is an RFC how files should look like. They aren't yet generated. --- Makefile.am | 5 +- drivers/qmimodem/qmi.h | 13 ++ drivers/qmimodem/voice.c | 86 ++++++++ drivers/qmimodem/voice.h | 84 ++++++++ drivers/qmimodem/voice_generated.c | 210 ++++++++++++++++++ drivers/qmimodem/voice_generated.h | 113 ++++++++++ drivers/qmimodem/voicecall.c | 333 ++++++++++++++++++++++++++++- 7 files changed, 841 insertions(+), 3 deletions(-) create mode 100644 drivers/qmimodem/voice.c create mode 100644 drivers/qmimodem/voice.h create mode 100644 drivers/qmimodem/voice_generated.c create mode 100644 drivers/qmimodem/voice_generated.h diff --git a/Makefile.am b/Makefile.am index 36101372..0ea9a721 100644 --- a/Makefile.am +++ b/Makefile.am @@ -216,7 +216,9 @@ qmi_sources = drivers/qmimodem/qmi.h drivers/qmimodem/qmi.c \ drivers/qmimodem/wds.h \ drivers/qmimodem/pds.h \ drivers/qmimodem/common.h \ - drivers/qmimodem/wda.h + drivers/qmimodem/wda.h \ + drivers/qmimodem/voice.h \ + drivers/qmimodem/voice.c builtin_modules += qmimodem builtin_sources += $(qmi_sources) \ @@ -225,6 +227,7 @@ builtin_sources += $(qmi_sources) \ drivers/qmimodem/qmimodem.c \ drivers/qmimodem/devinfo.c \ drivers/qmimodem/voicecall.c \ + drivers/qmimodem/voice_generated.c \ drivers/qmimodem/network-registration.c \ drivers/qmimodem/sim-legacy.c \ drivers/qmimodem/sim.c \ diff --git a/drivers/qmimodem/qmi.h b/drivers/qmimodem/qmi.h index b4955b40..71d9fc88 100644 --- a/drivers/qmimodem/qmi.h +++ b/drivers/qmimodem/qmi.h @@ -19,6 +19,9 @@ * */ +#ifndef __OFONO_QMI_QMI_H +#define __OFONO_QMI_QMI_H + #include #include @@ -172,3 +175,13 @@ uint16_t qmi_service_register(struct qmi_service *service, void *user_data, qmi_destroy_func_t destroy); bool qmi_service_unregister(struct qmi_service *service, uint16_t id); bool qmi_service_unregister_all(struct qmi_service *service); + + +/* FIXME: find a place for parse_error */ +enum parse_error { + NONE = 0, + MISSING_MANDATORY = 1, + INVALID_LENGTH = 2, +}; + +#endif /* __OFONO_QMI_QMI_H */ diff --git a/drivers/qmimodem/voice.c b/drivers/qmimodem/voice.c new file mode 100644 index 00000000..c0856176 --- /dev/null +++ b/drivers/qmimodem/voice.c @@ -0,0 +1,86 @@ +/* + * + * oFono - Open Source Telephony + * + * Copyright (C) 2017 Alexander Couzens + * + * 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. + * + */ + +#include + +#include "voice.h" +#include "../../src/common.h" + +#define _(X) case X: return #X + +const char *qmi_voice_call_state_name(enum qmi_voice_call_state value) +{ + switch (value) { + _(QMI_CALL_STATE_IDLE); + _(QMI_CALL_STATE_ORIG); + _(QMI_CALL_STATE_INCOMING); + _(QMI_CALL_STATE_CONV); + _(QMI_CALL_STATE_CC_IN_PROG); + _(QMI_CALL_STATE_ALERTING); + _(QMI_CALL_STATE_HOLD); + _(QMI_CALL_STATE_WAITING); + _(QMI_CALL_STATE_DISCONNECTING); + _(QMI_CALL_STATE_END); + _(QMI_CALL_STATE_SETUP); + } + return "QMI_CALL_STATE_"; +} + +int qmi_to_ofono_status(uint8_t status, int *ret) { + int err = 0; + switch (status) { + case QMI_CALL_STATE_IDLE: + case QMI_CALL_STATE_END: + case QMI_CALL_STATE_DISCONNECTING: + *ret = CALL_STATUS_DISCONNECTED; + break; + case QMI_CALL_STATE_HOLD: + *ret = CALL_STATUS_HELD; + break; + case QMI_CALL_STATE_WAITING: + *ret = CALL_STATUS_WAITING; + break; + case QMI_CALL_STATE_ORIG: + *ret = CALL_STATUS_DIALING; + break; + case QMI_CALL_STATE_INCOMING: + *ret = CALL_STATUS_INCOMING; + break; + case QMI_CALL_STATE_CONV: + *ret = CALL_STATUS_ACTIVE; + break; + case QMI_CALL_STATE_CC_IN_PROG: + case QMI_CALL_STATE_SETUP: + /* FIXME: unsure if _SETUP is dialing or not */ + *ret = CALL_STATUS_DIALING; + break; + case QMI_CALL_STATE_ALERTING: + *ret = CALL_STATUS_ALERTING; + break; + default: + err = 1; + } + return err; +} + +uint8_t ofono_to_qmi_direction(enum call_direction ofono_direction) { + return ofono_direction + 1; +} +enum call_direction qmi_to_ofono_direction(uint8_t qmi_direction) { + return qmi_direction - 1; +} + diff --git a/drivers/qmimodem/voice.h b/drivers/qmimodem/voice.h new file mode 100644 index 00000000..f25648f7 --- /dev/null +++ b/drivers/qmimodem/voice.h @@ -0,0 +1,84 @@ +/* + * + * oFono - Open Source Telephony + * + * Copyright (C) 2017 Alexander Couzens + * + * 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. + * + */ + +enum call_direction; + +enum ussd_dcs { + USS_DCS_ASCII = 0x1, + USS_DCS_8BIT, + USS_DCS_UCS2, +}; + +enum ussd_user_required { + NO_USER_ACTION_REQUIRED = 0x1, + USER_ACTION_REQUIRED = 0x2, +}; + +struct qmi_ussd_data { + uint8_t dcs; + uint8_t length; + uint8_t data[0]; +} __attribute__((__packed__)); + +enum voice_commands { + QMI_VOICE_CANCEL_USSD = 0x3c, + QMI_VOICE_USSD_RELEASE_IND = 0x3d, + QMI_VOICE_USSD_IND = 0x3e, + QMI_VOICE_SUPS_IND = 0x42, + QMI_VOICE_ASYNC_ORIG_USSD = 0x43, +}; + +enum qmi_voice_call_state { + QMI_CALL_STATE_IDLE = 0x0, + QMI_CALL_STATE_ORIG, + QMI_CALL_STATE_INCOMING, + QMI_CALL_STATE_CONV, + QMI_CALL_STATE_CC_IN_PROG, + QMI_CALL_STATE_ALERTING, + QMI_CALL_STATE_HOLD, + QMI_CALL_STATE_WAITING, + QMI_CALL_STATE_DISCONNECTING, + QMI_CALL_STATE_END, + QMI_CALL_STATE_SETUP +}; + +enum qmi_voice_call_type { + QMI_CALL_TYPE_VOICE = 0x0, + QMI_CALL_TYPE_VOICE_FORCE, +}; + +const char *qmi_voice_call_state_name(enum qmi_voice_call_state value); +uint8_t ofono_to_qmi_direction(enum call_direction ofono_direction); +enum call_direction qmi_to_ofono_direction(uint8_t qmi_direction); +int qmi_to_ofono_status(uint8_t status, int *ret); + +#define QMI_VOICE_IND_ALL_STATUS 0x2e + +#define QMI_VOICE_PARAM_USS_DATA 0x01 + +#define QMI_VOICE_PARAM_ASYNC_USSD_ERROR 0x10 +#define QMI_VOICE_PARAM_ASYNC_USSD_FAILURE_CASE 0x11 +#define QMI_VOICE_PARAM_ASYNC_USSD_DATA 0x12 + +#define QMI_VOICE_PARAM_USSD_IND_USER_ACTION 0x01 +#define QMI_VOICE_PARAM_USSD_IND_DATA 0x10 +#define QMI_VOICE_PARAM_USSD_IND_UCS2 0x11 + +/* according to GSM TS 23.038 */ +#define USSD_DCS_8BIT 0xf4 +#define USSD_DCS_UCS2 0x48 +#define USSD_DCS_UNSPECIFIC 0x0f diff --git a/drivers/qmimodem/voice_generated.c b/drivers/qmimodem/voice_generated.c new file mode 100644 index 00000000..3440be43 --- /dev/null +++ b/drivers/qmimodem/voice_generated.c @@ -0,0 +1,210 @@ + +#include +#include +#include + +#include "voice_generated.h" + +int qmi_voice_dial_call( + struct qmi_voice_dial_call_arg *arg, + struct qmi_service *service, + qmi_result_func_t func, + void *user_data, + qmi_destroy_func_t destroy) +{ + struct qmi_param *param = NULL; + + param = qmi_param_new(); + if (!param) + goto error; + + if (arg->calling_number_set) { + if (!qmi_param_append(param, + 0x1, + strlen(arg->calling_number), + arg->calling_number)) + goto error; + } + + if (arg->call_type_set) + qmi_param_append_uint8(param, 0x10, arg->call_type); + + if (qmi_service_send(service, + 0x20, + param, + func, + user_data, + destroy) > 0) + return 0; +error: + g_free(param); + return 1; +} + +enum parse_error qmi_voice_dial_call_parse( + struct qmi_result *qmi_result, + struct qmi_voice_dial_call_result *result) +{ + int err = NONE; + + /* mandatory */ + if (qmi_result_get_uint8(qmi_result, 0x10, &result->call_id)) + result->call_id_set = 1; + else + err = MISSING_MANDATORY; + + return err; +} + +int qmi_voice_end_call( + struct qmi_voice_end_call_arg *arg, + struct qmi_service *service, + qmi_result_func_t func, + void *user_data, + qmi_destroy_func_t destroy) +{ + struct qmi_param *param = NULL; + + param = qmi_param_new(); + if (!param) + goto error; + + if (arg->call_id_set) { + if (!qmi_param_append_uint8( + param, + 0x1, + arg->call_id)) + goto error; + } + + if (qmi_service_send(service, + 0x21, + param, + func, + user_data, + destroy) > 0) + return 0; +error: + g_free(param); + return 1; +} + +enum parse_error qmi_voice_end_call_parse( + struct qmi_result *qmi_result, + struct qmi_voice_end_call_result *result) +{ + int err = NONE; + + /* optional */ + if (qmi_result_get_uint8(qmi_result, 0x10, &result->call_id)) + result->call_id_set = 1; + + return err; +} + + +int qmi_voice_answer_call( + struct qmi_voice_answer_call_arg *arg, + struct qmi_service *service, + qmi_result_func_t func, + void *user_data, + qmi_destroy_func_t destroy) +{ + struct qmi_param *param = NULL; + + param = qmi_param_new(); + if (!param) + goto error; + + if (arg->call_id_set) { + if (!qmi_param_append_uint8( + param, + 0x1, + arg->call_id)) + goto error; + } + + if (qmi_service_send(service, + 0x22, + param, + func, + user_data, + destroy) > 0) + return 0; +error: + g_free(param); + return 1; +} + + +enum parse_error qmi_voice_answer_call_parse( + struct qmi_result *qmi_result, + struct qmi_voice_answer_call_result *result) +{ + int err = NONE; + + /* optional */ + if (qmi_result_get_uint8(qmi_result, 0x10, &result->call_id)) + result->call_id_set = 1; + + return err; +} + +enum parse_error qmi_voice_ind_call_status( + struct qmi_result *qmi_result, + struct qmi_voice_all_call_status_ind *result) +{ + int err = NONE; + int offset; + uint16_t len; + const struct qmi_voice_remote_party_number *remote_party_number; + const struct qmi_voice_call_information *call_information; + + /* mandatory */ + call_information = qmi_result_get(qmi_result, 0x01, &len); + if (call_information) + { + int instance_size = sizeof(struct qmi_voice_call_information_instance); + /* verify the length */ + if (len < sizeof(call_information->size)) + return INVALID_LENGTH; + + if (len != call_information->size * sizeof(struct qmi_voice_call_information_instance) + + sizeof(call_information->size)) + return INVALID_LENGTH; + result->call_information_set = 1; + result->call_information = call_information; + } else + return MISSING_MANDATORY; + + /* mandatory */ + remote_party_number = qmi_result_get(qmi_result, 0x10, &len); + if (remote_party_number) { + const struct qmi_voice_remote_party_number_instance *instance; + int instance_size = sizeof(struct qmi_voice_remote_party_number_instance); + int i; + + /* verify the length */ + if (len < sizeof(remote_party_number->size)) + return INVALID_LENGTH; + + for (i = 0, offset = sizeof(remote_party_number->size); + offset <= len && i < 16 && i < remote_party_number->size; i++) + { + if (offset == len) { + break; + } else if (offset + instance_size > len) { + return INVALID_LENGTH; + } + + instance = (void *)remote_party_number + offset; + result->remote_party_number[i] = instance; + offset += sizeof(struct qmi_voice_remote_party_number_instance) + instance->number_size; + } + result->remote_party_number_set = 1; + result->remote_party_number_size = remote_party_number->size; + } else + return MISSING_MANDATORY; + + return err; +} diff --git a/drivers/qmimodem/voice_generated.h b/drivers/qmimodem/voice_generated.h new file mode 100644 index 00000000..471b52ea --- /dev/null +++ b/drivers/qmimodem/voice_generated.h @@ -0,0 +1,113 @@ + +#ifndef __OFONO_QMI_VOICE_GENERATED_H +#define __OFONO_QMI_VOICE_GENERATED_H + +#include "qmi.h" + +struct qmi_voice_remote_party_number_instance { + uint8_t call_id; + uint8_t presentation_indicator; + uint8_t number_size; + char number[0]; +} __attribute__((__packed__)); + +struct qmi_voice_remote_party_number { + uint8_t size; + struct qmi_voice_remote_party_number_instance instance[0]; +} __attribute__((__packed__)); + +/* generator / parser */ + +struct qmi_voice_dial_call_arg { + bool calling_number_set; + const char *calling_number; + bool call_type_set; + uint8_t call_type; +}; + +int qmi_voice_dial_call( + struct qmi_voice_dial_call_arg *arg, + struct qmi_service *service, + qmi_result_func_t func, + void *user_data, + qmi_destroy_func_t destroy); + +struct qmi_voice_dial_call_result { + bool call_id_set; + uint8_t call_id; +}; + +enum parse_error qmi_voice_dial_call_parse( + struct qmi_result *qmi_result, + struct qmi_voice_dial_call_result *result); + +struct qmi_voice_end_call_arg { + bool call_id_set; + uint8_t call_id; +}; + +int qmi_voice_end_call( + struct qmi_voice_end_call_arg *arg, + struct qmi_service *service, + qmi_result_func_t func, + void *user_data, + qmi_destroy_func_t destroy); + +struct qmi_voice_end_call_result { + bool call_id_set; + uint8_t call_id; +}; + +enum parse_error qmi_voice_end_call_parse( + struct qmi_result *qmi_result, + struct qmi_voice_end_call_result *result); + +struct qmi_voice_answer_call_arg { + bool call_id_set; + uint8_t call_id; +}; + +int qmi_voice_answer_call( + struct qmi_voice_answer_call_arg *arg, + struct qmi_service *service, + qmi_result_func_t func, + void *user_data, + qmi_destroy_func_t destroy); + +struct qmi_voice_answer_call_result { + bool call_id_set; + uint8_t call_id; +}; + +enum parse_error qmi_voice_answer_call_parse( + struct qmi_result *qmi_result, + struct qmi_voice_answer_call_result *result); + +struct qmi_voice_call_information_instance { + uint8_t id; + uint8_t state; + uint8_t type; + uint8_t direction; + uint8_t mode; + uint8_t multipart_indicator; + uint8_t als; +} __attribute__((__packed__)); + +struct qmi_voice_call_information { + uint8_t size; + struct qmi_voice_call_information_instance instance[0]; +} __attribute__((__packed__)) ; + +struct qmi_voice_all_call_status_ind { + bool call_information_set; + const struct qmi_voice_call_information *call_information; + bool remote_party_number_set; + uint8_t remote_party_number_size; + const struct qmi_voice_remote_party_number_instance *remote_party_number[16]; +}; + +enum parse_error qmi_voice_ind_call_status( + struct qmi_result *qmi_result, + struct qmi_voice_all_call_status_ind *result); + +#endif /* __OFONO_QMI_VOICE_GENERATED_H */ diff --git a/drivers/qmimodem/voicecall.c b/drivers/qmimodem/voicecall.c index 29166b08..6b3caba4 100644 --- a/drivers/qmimodem/voicecall.c +++ b/drivers/qmimodem/voicecall.c @@ -3,6 +3,7 @@ * oFono - Open Source Telephony * * Copyright (C) 2011-2012 Intel Corporation. All rights reserved. + * Copyright (C) 2017 Alexander Couzens * * 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 @@ -23,20 +24,113 @@ #include #endif +#include + #include #include #include +#include + +#include "../src/common.h" #include "qmi.h" - #include "qmimodem.h" +#include "voice.h" +#include "voice_generated.h" + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + + +/* qmi protocol */ + + +/* end of qmi */ struct voicecall_data { struct qmi_service *voice; uint16_t major; uint16_t minor; + GSList *call_list; + struct voicecall_static *vs; + struct ofono_phone_number dialed; }; +static void all_call_status_ind(struct qmi_result *result, void *user_data) +{ + struct ofono_voicecall *vc = user_data; + struct voicecall_data *vd = ofono_voicecall_get_data(vc); + GSList *calls = NULL; + int i; + int size = 0; + 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) { + DBG("Parsing of all call status indication failed"); + return; + } + + if (!status_ind.remote_party_number_set || !status_ind.call_information_set) { + DBG("Some required fields are not set"); + return; + } + + size = status_ind.call_information->size; + if (!size) { + DBG("No call informations received!"); + return; + } + + /* expect we have valid fields for every call */ + if (size != status_ind.remote_party_number_size) { + DBG("Not all fields have the same size"); + return; + } + + for (i = 0; i < size; i++) { + struct qmi_voice_call_information_instance call_info; + struct ofono_call *call; + const struct qmi_voice_remote_party_number_instance *remote_party = status_ind.remote_party_number[i]; + int number_size; + + call_info = status_ind.call_information->instance[i]; + call = g_new0(struct ofono_call, 1); + call->id = call_info.id; + call->direction = qmi_to_ofono_direction(call_info.direction); + + if (qmi_to_ofono_status(call_info.state, &call->status)) { + DBG("Ignore call id %d, because can not convert QMI state 0x%x to ofono.", + call_info.id, call_info.state); + continue; + } + DBG("Call %d in state %s(%d)", + call_info.id, + qmi_voice_call_state_name(call_info.state), + call_info.state); + + call->type = 0; /* always voice */ + number_size = remote_party->number_size; + if (number_size > OFONO_MAX_PHONE_NUMBER_LENGTH) + OFONO_MAX_PHONE_NUMBER_LENGTH; + strncpy(call->phone_number.number, remote_party->number, + number_size); + /* FIXME: set phone_number_type */ + + if (strlen(call->phone_number.number) > 0) + call->clip_validity = 0; + else + call->clip_validity = 2; + + calls = g_slist_insert_sorted(calls, call, ofono_call_compare); + } + + ofono_call_list_notify(vc, &vd->call_list, calls); +} + static void create_voice_cb(struct qmi_service *service, void *user_data) { struct ofono_voicecall *vc = user_data; @@ -58,6 +152,12 @@ static void create_voice_cb(struct qmi_service *service, void *user_data) data->voice = qmi_service_ref(service); + /* FIXME: we should call indication_register to ensure we get notified on call events. + * We rely at the moment on the default value of notifications + */ + qmi_service_register(data->voice, QMI_VOICE_IND_ALL_STATUS, + all_call_status_ind, vc, NULL); + ofono_voicecall_register(vc); } @@ -77,7 +177,6 @@ static int qmi_voicecall_probe(struct ofono_voicecall *vc, create_voice_cb, vc, NULL); return 0; - } static void qmi_voicecall_remove(struct ofono_voicecall *vc) @@ -92,13 +191,243 @@ static void qmi_voicecall_remove(struct ofono_voicecall *vc) qmi_service_unref(data->voice); + g_slist_free_full(data->call_list, g_free); + g_free(data); } + +static struct ofono_call *create_call(struct ofono_voicecall *vc, + enum call_direction direction, + enum call_status status, + const char *num, + int num_type, + int clip) +{ + return NULL; +} + +static void dial_cb(struct qmi_result *result, void *user_data) +{ + struct cb_data *cbd = user_data; + struct ofono_voicecall *vc = cbd->user; + struct voicecall_data *vd = ofono_voicecall_get_data(vc); + ofono_voicecall_cb_t cb = cbd->cb; + uint16_t error; + struct qmi_voice_dial_call_result dial_result; + struct ofono_call *call; + + if (qmi_result_set_error(result, &error)) { + DBG("QMI Error %d", error); + CALLBACK_WITH_FAILURE(cb, cbd->data); + return; + } + + if (NONE != qmi_voice_dial_call_parse(result, &dial_result)) { + DBG("Received invalid Result"); + CALLBACK_WITH_FAILURE(cb, cbd->data); + return; + } + + if (!dial_result.call_id_set) { + DBG("Didn't receive a call id"); + CALLBACK_WITH_FAILURE(cb, cbd->data); + return; + } + + DBG("New call QMI id %d", dial_result.call_id); + ofono_call_list_dial_callback(vc, + &vd->call_list, + &vd->dialed, + dial_result.call_id); + + + /* FIXME: create a timeout on this call_id */ + CALLBACK_WITH_SUCCESS(cb, cbd->data); +} + +static void dial(struct ofono_voicecall *vc, const struct ofono_phone_number *ph, + enum ofono_clir_option clir, ofono_voicecall_cb_t cb, + void *data) +{ + struct voicecall_data *vd = ofono_voicecall_get_data(vc); + struct cb_data *cbd = cb_data_new(cb, data); + struct qmi_voice_dial_call_arg arg; + + cbd->user = vc; + arg.calling_number_set = true; + arg.calling_number = ph->number; + memcpy(&vd->dialed, ph, sizeof(*ph)); + + arg.call_type_set = true; + arg.call_type = QMI_CALL_TYPE_VOICE_FORCE; + + if (!qmi_voice_dial_call( + &arg, + vd->voice, + dial_cb, + cbd, + g_free)) + return; + + CALLBACK_WITH_FAILURE(cb, data); + g_free(cbd); +} + +static void answer_cb(struct qmi_result *result, void *user_data) +{ + struct cb_data *cbd = user_data; + struct ofono_voicecall *vc = cbd->user; + ofono_voicecall_cb_t cb = cbd->cb; + uint16_t error; + struct qmi_voice_answer_call_result answer_result; + struct ofono_call *call; + + if (qmi_result_set_error(result, &error)) { + DBG("QMI Error %d", error); + CALLBACK_WITH_FAILURE(cb, cbd->data); + return; + } + + /* TODO: what happens when calling it with no active call or wrong caller id? */ + if (NONE != qmi_voice_answer_call_parse(result, &answer_result)) { + DBG("Received invalid Result"); + CALLBACK_WITH_FAILURE(cb, cbd->data); + return; + } + + CALLBACK_WITH_SUCCESS(cb, cbd->data); +} + +static void answer(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data) +{ + struct voicecall_data *vd = ofono_voicecall_get_data(vc); + struct cb_data *cbd = cb_data_new(cb, data); + struct qmi_voice_answer_call_arg arg; + struct ofono_call *call; + GSList *list; + + DBG(""); + cbd->user = vc; + + list = g_slist_find_custom(vd->call_list, + GINT_TO_POINTER(CALL_STATUS_INCOMING), + ofono_call_compare_by_status); + + if (list == NULL) { + DBG("Can not find a call to answer"); + goto err; + } + + call = list->data; + + arg.call_id_set = true; + arg.call_id = call->id; + + if (!qmi_voice_answer_call( + &arg, + vd->voice, + answer_cb, + cbd, + g_free)) + return; +err: + CALLBACK_WITH_FAILURE(cb, data); + g_free(cbd); +} + +static void end_cb(struct qmi_result *result, void *user_data) +{ + struct cb_data *cbd = user_data; + struct ofono_voicecall *vc = cbd->user; + ofono_voicecall_cb_t cb = cbd->cb; + uint16_t error; + struct qmi_voice_end_call_result end_result; + struct ofono_call *call; + + if (qmi_result_set_error(result, &error)) { + DBG("QMI Error %d", error); + CALLBACK_WITH_FAILURE(cb, cbd->data); + return; + } + + if (NONE != qmi_voice_end_call_parse(result, &end_result)) { + DBG("Received invalid Result"); + CALLBACK_WITH_FAILURE(cb, cbd->data); + return; + } + + CALLBACK_WITH_SUCCESS(cb, cbd->data); +} + +static void release_specific(struct ofono_voicecall *vc, int id, + ofono_voicecall_cb_t cb, void *data) +{ + struct voicecall_data *vd = ofono_voicecall_get_data(vc); + struct cb_data *cbd = cb_data_new(cb, data); + struct qmi_voice_end_call_arg arg; + int i; + + DBG(""); + cbd->user = vc; + + arg.call_id_set = true; + arg.call_id = id; + + if (!qmi_voice_end_call(&arg, + vd->voice, + end_cb, + cbd, + g_free)) + return; + + CALLBACK_WITH_FAILURE(cb, data); + g_free(cbd); +} + +static void hangup_active(struct ofono_voicecall *vc, + ofono_voicecall_cb_t cb, void *data) +{ + struct voicecall_data *vd = ofono_voicecall_get_data(vc); + struct qmi_voice_end_call_arg arg; + struct ofono_call *call; + GSList *list = NULL; + enum call_status active[] = { + CALL_STATUS_ACTIVE, + CALL_STATUS_DIALING, + CALL_STATUS_ALERTING, + CALL_STATUS_INCOMING, + }; + int i; + + DBG(""); + for (i = 0; i < ARRAY_SIZE(active); i++) { + list = g_slist_find_custom(vd->call_list, + GINT_TO_POINTER(active[i]), + ofono_call_compare_by_status); + + if (list) + break; + } + + if (list == NULL) { + DBG("Can not find a call to hang up"); + CALLBACK_WITH_FAILURE(cb, data); + return; + } + + call = list->data; + release_specific(vc, call->id, cb, data); +} + static struct ofono_voicecall_driver driver = { .name = "qmimodem", .probe = qmi_voicecall_probe, .remove = qmi_voicecall_remove, + .dial = dial, + .answer = answer, + .hangup_active = hangup_active, + .release_specific = release_specific, }; void qmi_voicecall_init(void) From 3d66046ab765aba248d3f2eea1255f6f51d7cc29 Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Tue, 22 Aug 2017 13:47:48 +0200 Subject: [PATCH 57/60] call gprs_detached_notify --- src/gprs.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gprs.c b/src/gprs.c index 420c96f9..3f7e530a 100644 --- a/src/gprs.c +++ b/src/gprs.c @@ -2601,6 +2601,9 @@ void ofono_gprs_status_notify(struct ofono_gprs *gprs, int status) gprs->status = status; + if (status == 0) + ofono_gprs_detached_notify(gprs); + if (status != NETWORK_REGISTRATION_STATUS_REGISTERED && status != NETWORK_REGISTRATION_STATUS_ROAMING) { gprs_attached_update(gprs); From 8b23ce5d19556d526d2fe2da2378511b1b44804a Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Mon, 21 Aug 2017 04:32:19 +0200 Subject: [PATCH 58/60] qmi/discovery: remove useless code --- drivers/qmimodem/qmi.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/drivers/qmimodem/qmi.c b/drivers/qmimodem/qmi.c index d682775a..c2bc158b 100644 --- a/drivers/qmimodem/qmi.c +++ b/drivers/qmimodem/qmi.c @@ -1166,11 +1166,6 @@ static void discover_callback(uint16_t message, uint16_t length, 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: device->version_list = list; device->version_count = count; From 7f3704dea8a1888c187b40244bd55240aff6ad7f Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Mon, 21 Aug 2017 04:36:14 +0200 Subject: [PATCH 59/60] qmi/discovery: include control service into service list There is no need to exclude it. It helps debugging because the service is now logged. --- drivers/qmimodem/qmi.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/drivers/qmimodem/qmi.c b/drivers/qmimodem/qmi.c index c2bc158b..efd6a1da 100644 --- a/drivers/qmimodem/qmi.c +++ b/drivers/qmimodem/qmi.c @@ -1140,15 +1140,12 @@ static void discover_callback(uint16_t message, uint16_t length, if (type == QMI_SERVICE_CONTROL) { device->control_major = major; device->control_minor = minor; - continue; } - list[count].type = type; - list[count].major = major; - list[count].minor = minor; - list[count].name = name; - - count++; + list[i].type = type; + list[i].major = major; + list[i].minor = minor; + list[i].name = name; if (name) __debug_device(device, "found service [%s %d.%d]", @@ -1157,6 +1154,7 @@ static void discover_callback(uint16_t message, uint16_t length, __debug_device(device, "found service [%d %d.%d]", type, major, minor); } + count = service_list->count; ptr = tlv_get(buffer, length, 0x10, &len); if (!ptr) From b4cf29adaf1429e7e0c3292c66ca0f0ae2ecdacb Mon Sep 17 00:00:00 2001 From: Alexander Couzens Date: Tue, 25 Jul 2017 20:36:00 +0200 Subject: [PATCH 60/60] qmimodem: sync the modem on enable The qmi sync call release all previous resources. qmi/discovery: only do the sync call when ctl service version >= 1.5 --- drivers/qmimodem/qmi.c | 47 ++++++++++++++++++++++++++++++++++++++++++ drivers/qmimodem/qmi.h | 5 ++++- plugins/gobi.c | 32 ++++++++++++++++++++++++++-- 3 files changed, 81 insertions(+), 3 deletions(-) diff --git a/drivers/qmimodem/qmi.c b/drivers/qmimodem/qmi.c index efd6a1da..736a3217 100644 --- a/drivers/qmimodem/qmi.c +++ b/drivers/qmimodem/qmi.c @@ -1320,6 +1320,53 @@ bool qmi_device_shutdown(struct qmi_device *device, qmi_shutdown_func_t func, return true; } +struct sync_data { + qmi_sync_func_t func; + void *user_data; +}; + +static void qmi_device_sync_callback(uint16_t message, uint16_t length, + const void *buffer, void *user_data) +{ + struct sync_data *data = user_data; + + if(data->func) + data->func(data->user_data); + + g_free(data); +} + +/* sync will release all previous clients */ +bool qmi_device_sync(struct qmi_device *device, + qmi_sync_func_t func, void *user_data) +{ + struct qmi_request *req; + struct qmi_control_hdr *hdr; + struct sync_data *func_data; + + if (!device) + return false; + + func_data = g_new0(struct sync_data, 1); + func_data->func = func; + func_data->user_data = user_data; + + req = __request_alloc(QMI_SERVICE_CONTROL, 0x00, + QMI_CTL_SYNC, QMI_CONTROL_HDR_SIZE, + NULL, 0, + qmi_device_sync_callback, func_data, (void **) &hdr); + + if (device->next_control_tid < 1) + device->next_control_tid = 1; + + hdr->type = 0x00; + hdr->transaction = device->next_control_tid++; + + __request_submit(device, req, hdr->transaction); + + return true; +} + static bool get_device_file_name(struct qmi_device *device, char *file_name, int size) { diff --git a/drivers/qmimodem/qmi.h b/drivers/qmimodem/qmi.h index 71d9fc88..1229ab13 100644 --- a/drivers/qmimodem/qmi.h +++ b/drivers/qmimodem/qmi.h @@ -79,7 +79,7 @@ typedef void (*qmi_destroy_func_t)(void *user_data); struct qmi_device; typedef void (*qmi_debug_func_t)(const char *str, void *user_data); - +typedef void (*qmi_sync_func_t)(void *user_data); typedef void (*qmi_shutdown_func_t)(void *user_data); typedef void (*qmi_discover_func_t)(uint8_t count, const struct qmi_version *list, void *user_data); @@ -99,6 +99,9 @@ bool qmi_device_discover(struct qmi_device *device, qmi_discover_func_t func, bool qmi_device_shutdown(struct qmi_device *device, qmi_shutdown_func_t func, void *user_data, qmi_destroy_func_t destroy); +bool qmi_device_sync(struct qmi_device *device, + qmi_sync_func_t func, void *user_data); + enum qmi_device_expected_data_format qmi_device_get_expected_data_format( struct qmi_device *device); bool qmi_device_set_expected_data_format(struct qmi_device *device, diff --git a/plugins/gobi.c b/plugins/gobi.c index a4985990..0c621ca2 100644 --- a/plugins/gobi.c +++ b/plugins/gobi.c @@ -252,6 +252,15 @@ error: 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, void *user_data) { @@ -266,6 +275,13 @@ static void discover_cb(uint8_t count, const struct qmi_version *list, 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: data->features |= GOBI_DMS; break; @@ -314,8 +330,20 @@ static void discover_cb(uint8_t count, const struct qmi_version *list, return; } - qmi_service_create_shared(data->device, QMI_SERVICE_DMS, - create_dms_cb, modem, NULL); + if (ofono_modem_get_boolean(modem, "SupportQMISync") == TRUE) + 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)