Compare commits

...

45 Commits

Author SHA1 Message Date
Slava Monich
fc696fc9fa [ofono] Access control for SIM Toolkit agent. Fixes JB#49163
Non-privileged process will get org.ofono.Error.AccessDenied from
RegisterAgent. Other methods already check that D-Bus call is coming
from a registered agent.
2020-03-03 23:42:28 +02:00
Slava Monich
aa4309e8cb Merge branch 'once' into 'master'
Store one-time data SIM selection

See merge request mer-core/ofono!251
2020-02-13 17:05:02 +00:00
Slava Monich
cb6b24d950 [ofono] Store one-time data SIM selection. JB#48462
This way, automatic choice survives a reboot.
2020-02-13 18:53:32 +03:00
Slava Monich
6d1ab13c74 [ofono] Ignore known deprecation warnings
Apparently, the only way to get rid of "warning: G_ADD_PRIVATE" is to
completely disable all Glib deprecation warnings in the entire file.
G_GNUC_BEGIN/END_IGNORE_DEPRECATIONS macros don't help :/
2020-02-12 20:58:24 +03:00
Slava Monich
45424a3f96 Merge branch 'error55' into 'master'
Workaround for data call status 55

See merge request mer-core/ofono!250
2020-02-12 17:05:43 +00:00
Slava Monich
4f7398e39d [ril] Workaround for data call status 55. JB#40162
With some networks we sometimes start getting error 55 (Multiple
PDN connections for a given APN not allowed) when trying to setup
an LTE data call and this error doesn't go away until we successfully
establish a data call over 3G. Then we can switch back to LTE.
2020-02-11 17:08:01 +03:00
Slava Monich
cd118ce70b [qmimodem] Move lte.c to the right place 2020-02-11 12:51:15 +03:00
Slava Monich
99d4ce538e [ofono] Updated baseline to 1.23. Fixes JB#48840 2020-02-05 17:05:33 +02:00
Slava Monich
9b2c4bcf76 Merge branch 'v1.23' into 'master'
Update baseline to 1.23

See merge request mer-core/ofono!247
2020-02-05 14:59:54 +00:00
Slava Monich
0122db04a3 Revert "[unit] Fixed memory leak in test-simutil"
This reverts commit c04b14c49a.

It conflicts with commit f5971198 cherry-picked from upstream.
2020-02-05 16:10:42 +02:00
Marcel Holtmann
021db194cb Release 1.23 2020-02-05 15:49:23 +02:00
Jonas Bonn
e0a0896205 qmimodem: release DMS service on radio-settings atom removal 2020-02-05 15:49:23 +02:00
Jonas Bonn
49d0bbbb28 qmimodem: release WDS service on GPRS atom removal 2020-02-05 15:49:22 +02:00
Jonas Bonn
9193d06b77 qmimodem: get LTE default bearer APN from modem
When an LTE modem registers with the network, a default bearer is
automatically established.  The APN used for this bearer is taken from
whatever default settings the modem has.

The LTE atom takes cares of setting up the default context/profile with
the APN to use.  From there, a default bearer will be established when
the modem registers with the network.  This results in a call to 'Get
LTE Attach Parameters' which tells us what APN the gateway negotiated
with us.

If we can't get the APN, we do what the AT driver does:  pretend the
bearer wasn't established.  This is a reasonable fallback, currently,
because connman can't handle zero-length APN's anyway; the previous
approach of setting the APN to 'automatic' breaks connman badly when it
needs to switch between LTE and non-LTE networks.
2020-02-05 15:49:22 +02:00
Jonas Bonn
b0cd3e4544 gobi: add LTE atom
This atom needs to be created in post_sim so that the APN can be
written to the default profile before the modem attempts to use the
setting to connect to the network.
2020-02-05 15:49:22 +02:00
Jonas Bonn
29cce6969b qmi: add LTE atom driver
This patch adds an LTE atom for QMI modems.

This atom sets the APN that the LTE default bearer should use when
establishing its PDP context.  This APN needs to be set on the 'default'
profile so the atom queries which profile is the default and resets
it before allowing the APN to be set.

Once configured, the default profile settings are used when the
modem connects to the network; for this reason, the LTE atom needs
to be instantiated in post_sim, before the modem is set online.
2020-02-05 15:49:22 +02:00
Denis Kenzior
b87f666e4b sim-auth: Improve pending cleanup on sim_auth_remove 2020-02-05 15:49:21 +02:00
Denis Kenzior
1c1e4fa28b sim-auth: Do not leak nai
==31530== 88 bytes in 2 blocks are definitely lost in loss record 132 of 186
==31530==    at 0x4C2BF8F: malloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==31530==    by 0x5847B97: vasprintf (in /lib64/libc-2.23.so)
==31530==    by 0x510AE38: g_vasprintf (gprintf.c:316)
==31530==    by 0x50D8BDF: g_strdup_vprintf (gstrfuncs.c:514)
==31530==    by 0x50D8CAA: g_strdup_printf (gstrfuncs.c:540)
==31530==    by 0x4F706B: build_nai (sim-auth.c:660)
==31530==    by 0x4F706B: sim_auth_register (sim-auth.c:738)
==31530==    by 0x4F706B: ofono_sim_auth_create (sim-auth.c:768)
==31530==    by 0x4ACBB4: modem_change_state (modem.c:525)
==31530==    by 0x4AD0CD: sim_state_watch.part.5 (modem.c:720)
==31530==    by 0x4CF6D0: call_state_watches (sim.c:366)
==31530==    by 0x4CF6D0: sim_set_ready (sim.c:1475)
==31530==    by 0x4CF6D0: sim_imsi_obtained (sim.c:1577)
==31530==    by 0x45D868: at_cimi_cb (sim.c:453)
==31530==    by 0x49CB5F: at_chat_finish_command (gatchat.c:459)
==31530==    by 0x49DAC7: at_chat_handle_command_response (gatchat.c:521)
==31530==    by 0x49DAC7: have_line (gatchat.c:600)
==31530==    by 0x49DAC7: new_bytes (gatchat.c:759)
2020-02-05 15:49:03 +02:00
Denis Kenzior
f597119845 unit: Use sim_app_record_free to avoid memleaks 2020-02-05 15:49:03 +02:00
Christophe Ronco
633888932d udevng: Add modem string SystemPath 2020-02-05 15:44:38 +02:00
Christophe Ronco
a37f325d4a modem: Add SystemPath dbus property 2020-02-05 15:44:38 +02:00
Christophe Ronco
0afceac554 doc: Add SystemPath to Modem interface 2020-02-05 15:44:38 +02:00
Philippe De Swert
109751bcc0 doc: add new DialMemory method to VoicecallManager 2020-02-05 15:44:38 +02:00
Denis Kenzior
30dfbf8fd7 hfpmodem: Don't use strcat 2020-02-05 15:44:37 +02:00
Philippe De Swert
c7aab2e790 hfpmodem: Add memory dialling support
Handle the request to dial from a memory index and send the
correct ATD> sequence to make it happen.
2020-02-05 15:44:37 +02:00
Philippe De Swert
412a2a0e7f voicecall: Add memory location dialing
Implement functionality to allow to  dial favourites/quick contacts over
bluetooth.
2020-02-05 15:44:37 +02:00
Philippe De Swert
f1aeedd113 voicecall: Add support for dialing number at a given memory location
Add a new function to be able to dial numbers from memory/favourites.

Conflicts:
	ofono/include/voicecall.h
2020-02-05 15:44:32 +02:00
Philippe De Swert
639fce8eca voicecall: Rename hfp dialing functions
Calling from memory index is very similar in functionality to dialing
the last called number. So we rename the functions so we can reuse them,
to deal with memory index calling. Function names now also reflect this
is for hfp.
2020-02-05 15:34:26 +02:00
Jonas Bonn
9cfd0a195e xmm7modem: drop executable bit from C source file 2020-02-05 15:34:25 +02:00
Slava Monich
cf2d8488cc Merge branch 'dbm' into 'master'
Add signalStrengthRange option

See merge request mer-core/ofono!246
2020-01-22 10:10:44 +00:00
Slava Monich
ab0ac10abd [ril] Added signalStrengthRange option. JB#46086
Allows to tweak conversion of dBm values returned by the modem
into signal strength percentage.
2020-01-21 19:15:52 +02:00
Slava Monich
b2df7de223 Merge branch 'auto-select-data' into 'master'
Add AutoSelectDataSim option.

See merge request mer-core/ofono!245
2020-01-16 18:39:03 +00:00
Slava Monich
b56930a87f [unit] Updated test-sailfish_manager. JB#48462 2020-01-16 03:35:07 +02:00
Slava Monich
f65e8dd5af [ofono] Added AutoSelectDataSim option. JB#48462
To change the default behavior in respect to selecting the data SIM, the
following option can be added to /etc/ofono/main.conf:

[ModemManager]
AutoSelectDataSim=on

And data SIM will be picked automatically if none is selected by the user.
All possible options:

   off = option is off (default)
   once = automatically select data SIM once
   always = make sure that data SIM is always selected

"on" is equivalent to "always"
2020-01-16 03:34:26 +02:00
Slava Monich
f2439243b2 [ofono] ril_config_merge_files => config_merge_files
And moved to the core, to make it usable from RIL independent code.
2019-12-26 15:56:54 +02:00
Slava Monich
93ff644856 [ofono] Added __ofono_set_config_dir() function
Allows to make configuration directory configurable.
Also, useful for unit tests.
2019-12-26 02:35:09 +02:00
Slava Monich
08f8555d51 [packaging] Remove sailfish headers from the ofono-devel
Those symbols are not exported by ofono anyway and can't be
referenced by external plugins. Built-in plugins can pull those
headers directly from the source tree.
2019-12-19 16:51:23 +02:00
Slava Monich
04b2a9b0f9 Merge branch 'ecclist' into 'master'
Add support for MediaTek specific ril.ecclist syntax

See merge request mer-core/ofono!244
2019-11-25 11:26:17 +00:00
Slava Monich
4d513b68d2 [unit] Added unit test for ril_ecclist. JB#47953 2019-11-22 20:03:40 +02:00
Slava Monich
36f971dc78 [ril] Support MTK specific ril.ecclist syntax. JB#47953
Some MediaTek adaptations use semicolon as a separator between
comma-separated phone number/service category pairs, e.g.

  112,31;911,31;112,-1;911,-1
2019-11-22 20:03:24 +02:00
Slava Monich
8ba2d96cff [ril] Hosekeeping
gcc 8.3 noticed this:

ril_data.c: In function 'ril_data_call_list_equal':
ril_data.c:437:19: warning: self-comparison always evaluates to true [-Wtautological-compare]
    if (l1->version == l1->version && l1->num == l2->num) {
                    ^~
2019-11-22 19:51:38 +02:00
Slava Monich
a76f50be67 Merge branch 'vendor_signal' into 'master'
Add vendor-specific parsing of SIGNAL_STRENGTH messages

See merge request mer-core/ofono!242
2019-10-31 14:18:45 +00:00
Slava Monich
43227086c0 [ril] Added vendor-specific parsing of SIGNAL_STRENGTH messages. JB#47880
MediaTek loves inventing non-standard message formats.
2019-10-25 15:48:07 +03:00
Slava Monich
e40811fbc6 Merge branch 'gsm_when_radio_off' into 'master'
Add forceGsmWhenRadioOff config option

On some phones (such as Jolla C), even if the slot which has been
using LTE gets powered off, we still need to explicitly set its
preferred mode to GSM, to make LTE machinery available to the other
slot.

Some RILs don't like it when we do this.

See merge request mer-core/ofono!241
2019-10-21 10:27:29 +00:00
Slava Monich
8c543b054a [ril] Added forceGsmWhenRadioOff config option. JB#47747
On some phones (such as Jolla C), even if the slot which has been
using LTE gets powered off, we still need to explicitly set its
preferred mode to GSM, to make LTE machinery available to the other
slot.

Some RILs don't like it when we do this.
2019-10-16 16:48:19 +03:00
58 changed files with 2603 additions and 948 deletions

2
ofono/.gitignore vendored
View File

@@ -48,6 +48,7 @@ unit/test-dbus-access
unit/test-dbus-queue
unit/test-gprs-filter
unit/test-ril_config
unit/test-ril_ecclist
unit/test-ril_util
unit/test-ril_vendor
unit/test-ril-transport
@@ -61,6 +62,7 @@ unit/test-sailfish_cell_info_dbus
unit/test-sailfish_manager
unit/test-sailfish_sim_info
unit/test-sailfish_sim_info_dbus
unit/test-config
unit/test-watch
unit/test-sms-filter
unit/test-voicecall-filter

View File

@@ -1,3 +1,8 @@
ver 1.23:
Fix issue with handling SIM AID sessions.
Add support for QMI LTE bearer handling.
Add support for memory location dialing.
ver 1.22:
Fix issue with GPIO handling and Nokia modems.
Fix issue with SIM state callback and AT modems.

View File

@@ -32,11 +32,6 @@ pkginclude_HEADERS = include/log.h include/plugin.h include/history.h \
nodist_pkginclude_HEADERS = include/version.h
if SAILFISH_MANAGER
nodist_pkginclude_HEADERS += include/sailfish_cell_info.h \
include/sailfish_manager.h
endif
local_headers = $(foreach file,$(pkginclude_HEADERS) \
$(nodist_pkginclude_HEADERS), \
include/ofono/$(notdir $(file)))
@@ -310,6 +305,7 @@ builtin_sources += $(qmi_sources) \
drivers/qmimodem/ussd.c \
drivers/qmimodem/gprs.c \
drivers/qmimodem/gprs-context.c \
drivers/qmimodem/lte.c \
drivers/qmimodem/radio-settings.c \
drivers/qmimodem/location-reporting.c \
drivers/qmimodem/netmon.c
@@ -777,7 +773,7 @@ src_ofonod_SOURCES = $(builtin_sources) $(gatchat_sources) src/ofono.ver \
src/handsfree-audio.c src/bluetooth.h \
src/sim-mnclength.c src/voicecallagent.c \
src/sms-filter.c src/gprs-filter.c \
src/dbus-queue.c src/dbus-access.c \
src/dbus-queue.c src/dbus-access.c src/config.c \
src/voicecall-filter.c src/ril-transport.c \
src/hfp.h src/siri.c src/watchlist.c \
src/netmon.c src/lte.c src/ims.c \
@@ -808,7 +804,8 @@ 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$(srcdir)/plugins/sailfish_manager
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 \
@@ -972,8 +969,7 @@ if SAILFISH_MANAGER
unit_test_sailfish_cell_info_SOURCES = unit/test-sailfish_cell_info.c \
plugins/sailfish_manager/sailfish_cell_info.c
unit_test_sailfish_cell_info_CFLAGS = $(AM_CFLAGS) $(COVERAGE_OPT) \
-Iplugins/sailfish_manager
unit_test_sailfish_cell_info_CFLAGS = $(AM_CFLAGS) $(COVERAGE_OPT)
unit_test_sailfish_cell_info_LDADD = @GLIB_LIBS@ -ldl
unit_objects += $(unit_test_sailfish_cell_info_OBJECTS)
unit_tests += unit/test-sailfish_cell_info
@@ -986,7 +982,7 @@ unit_test_sailfish_cell_info_dbus_SOURCES = unit/test-dbus.c \
gdbus/object.c \
src/dbus.c src/log.c
unit_test_sailfish_cell_info_dbus_CFLAGS = $(AM_CFLAGS) $(COVERAGE_OPT) \
@DBUS_GLIB_CFLAGS@ -Iplugins/sailfish_manager
@DBUS_GLIB_CFLAGS@
unit_test_sailfish_cell_info_dbus_LDADD = @DBUS_GLIB_LIBS@ @GLIB_LIBS@ -ldl
unit_objects += $(unit_test_sailfish_cell_info_dbus_OBJECTS)
unit_tests += unit/test-sailfish_cell_info_dbus
@@ -996,7 +992,7 @@ unit_test_sailfish_sim_info_SOURCES = unit/test-sailfish_sim_info.c \
plugins/sailfish_manager/sailfish_sim_info.c \
src/storage.c src/watchlist.c src/log.c
unit_test_sailfish_sim_info_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS) \
-DSTORAGEDIR='"/tmp/ofono"' -Iplugins/sailfish_manager
-DSTORAGEDIR='"/tmp/ofono"'
unit_test_sailfish_sim_info_LDADD = @GLIB_LIBS@ -ldl
unit_objects += $(unit_test_sailfish_sim_info_OBJECTS)
unit_tests += unit/test-sailfish_sim_info
@@ -1008,8 +1004,7 @@ unit_test_sailfish_sim_info_dbus_SOURCES = unit/test-sailfish_sim_info_dbus.c \
gdbus/object.c \
src/dbus.c src/storage.c src/watchlist.c src/log.c
unit_test_sailfish_sim_info_dbus_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS) \
@DBUS_GLIB_CFLAGS@ -DSTORAGEDIR='"/tmp/ofono"' \
-Iplugins/sailfish_manager
@DBUS_GLIB_CFLAGS@ -DSTORAGEDIR='"/tmp/ofono"'
unit_test_sailfish_sim_info_dbus_LDADD = @DBUS_GLIB_LIBS@ @GLIB_LIBS@ -ldl
unit_objects += $(unit_test_sailfish_sim_info_dbus_OBJECTS)
unit_tests += unit/test-sailfish_sim_info_dbus
@@ -1021,7 +1016,7 @@ unit_test_sailfish_manager_SOURCES = unit/test-sailfish_manager.c \
plugins/sailfish_manager/sailfish_sim_info.c \
src/storage.c src/log.c
unit_test_sailfish_manager_CFLAGS = $(AM_CFLAGS) $(COVERAGE_OPT) \
-DSTORAGEDIR='"/tmp/ofono"' -Iplugins/sailfish_manager
-DSTORAGEDIR='"/tmp/ofono"'
unit_test_sailfish_manager_LDADD = @GLIB_LIBS@ -ldl
unit_objects += $(unit_test_sailfish_manager_OBJECTS)
unit_tests += unit/test-sailfish_manager
@@ -1029,13 +1024,20 @@ unit_tests += unit/test-sailfish_manager
unit_test_watch_SOURCES = unit/test-watch.c src/watch.c \
src/log.c src/watchlist.c
unit_test_watch_CFLAGS = $(AM_CFLAGS) $(COVERAGE_OPT) \
-DSTORAGEDIR='"/tmp/ofono"' -Iplugins/sailfish_manager
-DSTORAGEDIR='"/tmp/ofono"'
unit_test_watch_LDADD = @GLIB_LIBS@ -ldl
unit_objects += $(unit_test_watch_OBJECTS)
unit_tests += unit/test-watch
endif
unit_test_config_SOURCES = unit/test-config.c drivers/ril/ril_util.c \
src/config.c src/log.c
unit_test_config_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
unit_test_config_LDADD = @GLIB_LIBS@ -ldl
unit_objects += $(unit_test_config_OBJECTS)
unit_tests += unit/test-config
if SAILFISH_ACCESS
unit_test_sailfish_access_SOURCES = unit/test-sailfish_access.c \
plugins/sailfish_access.c src/dbus-access.c src/log.c
@@ -1062,6 +1064,13 @@ unit_test_ril_config_LDADD = @GLIB_LIBS@ -ldl
unit_objects += $(unit_test_ril_config_OBJECTS)
unit_tests += unit/test-ril_config
unit_test_ril_ecclist_SOURCES = unit/test-ril_ecclist.c \
drivers/ril/ril_ecclist.c src/log.c
unit_test_ril_ecclist_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
unit_test_ril_ecclist_LDADD = @GLIB_LIBS@ -ldl
unit_objects += $(unit_test_ril_ecclist_OBJECTS)
unit_tests += unit/test-ril_ecclist
unit_test_ril_util_SOURCES = unit/test-ril_util.c drivers/ril/ril_util.c \
src/log.c
unit_test_ril_util_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)

View File

@@ -1,5 +1,5 @@
AC_PREREQ(2.60)
AC_INIT(ofono, 1.22)
AC_INIT(ofono, 1.23)
AM_INIT_AUTOMAKE([foreign subdir-objects color-tests])
AC_CONFIG_HEADERS(config.h)

View File

@@ -95,6 +95,13 @@ Properties boolean Powered [readwrite]
String representing the software version number of the
modem device.
string SystemPath [readonly, optional]
String representing the system path for the modem
device.
For modems detected by udev events, this corresponds to
the modem sysfs path.
array{string} Features [readonly]
List of currently enabled features. It uses simple

View File

@@ -69,6 +69,17 @@ Methods dict GetProperties()
[service].Error.NotImplemented
[service].Error.Failed
object DialMemory(string memory position, string hide_callerid)
Initiates a new outgoing call to the number in the given memory
position/favourite. For callerid see the Dial method.
Possible Errors: [service].Error.InProgress
[service].Error.InvalidArguments
[service].Error.InvalidFormat
[service].Error.NotImplemented
[service].Error.Failed
void Transfer()
Joins the currently Active (or Outgoing, depending

View File

@@ -422,6 +422,28 @@ static void hfp_dial_last(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb,
CALLBACK_WITH_FAILURE(cb, data);
}
static void hfp_dial_memory(struct ofono_voicecall *vc,
unsigned int memory_location,
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);
char buf[256];
cbd->user = vc;
DBG("Calling memory location %d\n", memory_location);
snprintf(buf, sizeof(buf), "ATD>%d;", memory_location);
if (g_at_chat_send(vd->chat, buf, none_prefix,
atd_cb, cbd, g_free) > 0)
return;
g_free(cbd);
DBG("at_chat_failed");
CALLBACK_WITH_FAILURE(cb, data);
}
static void hfp_template(const char *cmd, struct ofono_voicecall *vc,
GAtResultFunc result_cb, unsigned int affected_types,
ofono_voicecall_cb_t cb, void *data)
@@ -1287,6 +1309,7 @@ static struct ofono_voicecall_driver driver = {
.remove = hfp_voicecall_remove,
.dial = hfp_dial,
.dial_last = hfp_dial_last,
.dial_memory = hfp_dial_memory,
.answer = hfp_answer,
.hangup_active = hfp_hangup,
.hold_all_active = hfp_hold_all_active,

View File

@@ -29,12 +29,16 @@
#include "qmi.h"
#include "nas.h"
#include "wds.h"
#include "src/common.h"
#include "qmimodem.h"
struct gprs_data {
struct qmi_device *dev;
struct qmi_service *nas;
struct qmi_service *wds;
unsigned int last_auto_context_id;
};
static bool extract_ss_info(struct qmi_result *result, int *status, int *tech)
@@ -64,8 +68,124 @@ static bool extract_ss_info(struct qmi_result *result, int *status, int *tech)
return true;
}
static void get_lte_attach_param_cb(struct qmi_result *result, void *user_data)
{
struct ofono_gprs *gprs = user_data;
struct gprs_data *data = ofono_gprs_get_data(gprs);
char *apn = NULL;
uint16_t error;
uint8_t iptype;
DBG("");
if (qmi_result_set_error(result, &error)) {
ofono_error("Failed to query LTE attach params: %hd", error);
goto noapn;
}
/* APN */
apn = qmi_result_get_string(result, 0x10);
if (!apn) {
DBG("Default profile has no APN setting");
goto noapn;
}
if (qmi_result_get_uint8(result, 0x11, &iptype))
ofono_info("LTE attach IP type: %hhd", iptype);
ofono_gprs_cid_activated(gprs, data->last_auto_context_id, apn);
g_free(apn);
return;
noapn:
data->last_auto_context_id = 0;
ofono_error("LTE bearer established but APN not set");
}
static void get_default_profile_cb(struct qmi_result *result, void *user_data)
{
struct ofono_gprs* gprs = user_data;
struct gprs_data *data = ofono_gprs_get_data(gprs);
uint16_t error;
uint8_t index;
DBG("");
if (qmi_result_set_error(result, &error)) {
ofono_error("Get default profile error: %hd", error);
goto error;
}
/* Profile index */
if (!qmi_result_get_uint8(result, 0x01, &index)) {
ofono_error("Failed query default profile");
goto error;
}
DBG("Default profile index: %hhd", index);
data->last_auto_context_id = index;
/* Get LTE Attach Parameters */
if (qmi_service_send(data->wds, 0x85, NULL,
get_lte_attach_param_cb, gprs, NULL) > 0)
return;
error:
data->last_auto_context_id = 0;
ofono_error("LTE bearer established but APN not set");
}
/*
* Query the settings in effect on the default bearer. These may be
* implicit or may even be something other than requested as the gateway
* is allowed to override whatever was requested by the user.
*/
static void get_lte_attach_params(struct ofono_gprs* gprs)
{
struct gprs_data *data = ofono_gprs_get_data(gprs);
struct {
uint8_t type;
uint8_t family;
} __attribute((packed)) p = {
.type = 0, /* 3GPP */
.family = 0, /* embedded */
};
struct qmi_param *param;
DBG("");
if (data->last_auto_context_id != 0)
return; /* Established or in progress */
/* Set query in progress */
data->last_auto_context_id = -1;
/* First we query the default profile in order to find out which
* context the modem has activated.
*/
param = qmi_param_new();
if (!param)
goto error;
/* Profile type */
qmi_param_append(param, 0x1, sizeof(p), &p);
/* Get default profile */
if (qmi_service_send(data->wds, 0x49, param,
get_default_profile_cb, gprs, NULL) > 0)
return;
qmi_param_free(param);
error:
ofono_warn("Unable to query LTE APN... will not activate context");
}
static int handle_ss_info(struct qmi_result *result, struct ofono_gprs *gprs)
{
struct gprs_data *data = ofono_gprs_get_data(gprs);
int status;
int tech;
@@ -74,17 +194,20 @@ static int handle_ss_info(struct qmi_result *result, struct ofono_gprs *gprs)
if (!extract_ss_info(result, &status, &tech))
return -1;
if (status == NETWORK_REGISTRATION_STATUS_REGISTERED)
if (status == NETWORK_REGISTRATION_STATUS_REGISTERED) {
if (tech == ACCESS_TECHNOLOGY_EUTRAN) {
/* On LTE we are effectively always attached; and
* the default bearer is established as soon as the
* network is joined.
* network is joined. We just need to query the
* parameters in effect on the default bearer and
* let the ofono core know about the activated
* context.
*/
/* FIXME: query default profile number and APN
* instead of assuming profile 1 and ""
*/
ofono_gprs_cid_activated(gprs, 1 , "automatic");
get_lte_attach_params(gprs);
}
} else {
data->last_auto_context_id = 0;
}
return status;
}
@@ -198,7 +321,7 @@ static void qmi_attached_status(struct ofono_gprs *gprs,
g_free(cbd);
}
static void create_nas_cb(struct qmi_service *service, void *user_data)
static void create_wds_cb(struct qmi_service *service, void *user_data)
{
struct ofono_gprs *gprs = user_data;
struct gprs_data *data = ofono_gprs_get_data(gprs);
@@ -206,12 +329,12 @@ static void create_nas_cb(struct qmi_service *service, void *user_data)
DBG("");
if (!service) {
ofono_error("Failed to request NAS service");
ofono_error("Failed to request WDS service");
ofono_gprs_remove(gprs);
return;
}
data->nas = qmi_service_ref(service);
data->wds = qmi_service_ref(service);
/*
* First get the SS info - the modem may already be connected,
@@ -228,6 +351,25 @@ static void create_nas_cb(struct qmi_service *service, void *user_data)
ofono_gprs_register(gprs);
}
static void create_nas_cb(struct qmi_service *service, void *user_data)
{
struct ofono_gprs *gprs = user_data;
struct gprs_data *data = ofono_gprs_get_data(gprs);
DBG("");
if (!service) {
ofono_error("Failed to request NAS service");
ofono_gprs_remove(gprs);
return;
}
data->nas = qmi_service_ref(service);
qmi_service_create_shared(data->dev, QMI_SERVICE_WDS,
create_wds_cb, gprs, NULL);
}
static int qmi_gprs_probe(struct ofono_gprs *gprs,
unsigned int vendor, void *user_data)
{
@@ -240,6 +382,8 @@ static int qmi_gprs_probe(struct ofono_gprs *gprs,
ofono_gprs_set_data(gprs, data);
data->dev = device;
qmi_service_create_shared(device, QMI_SERVICE_NAS,
create_nas_cb, gprs, NULL);
@@ -254,6 +398,9 @@ static void qmi_gprs_remove(struct ofono_gprs *gprs)
ofono_gprs_set_data(gprs, NULL);
qmi_service_unregister_all(data->wds);
qmi_service_unref(data->wds);
qmi_service_unregister_all(data->nas);
qmi_service_unref(data->nas);

View File

@@ -0,0 +1,264 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2018 Jonas Bonn. All rights reserved.
* Copyright (C) 2018 Norrbonn AB. All rights reserved.
* Copyright (C) 2018 Data Respons ASA. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <glib.h>
#include <ofono/modem.h>
#include <ofono/gprs-context.h>
#include <ofono/log.h>
#include <ofono/lte.h>
#include "qmi.h"
#include "wds.h"
#include "qmimodem.h"
struct lte_data {
struct qmi_service *wds;
uint8_t default_profile;
};
static void modify_profile_cb(struct qmi_result *result, void *user_data)
{
struct cb_data *cbd = user_data;
ofono_lte_cb_t cb = cbd->cb;
uint16_t error;
DBG("");
if (qmi_result_set_error(result, &error)) {
DBG("Failed to modify profile: %d", error);
CALLBACK_WITH_FAILURE(cb, cbd->data);
return;
}
CALLBACK_WITH_SUCCESS(cb, cbd->data);
}
static void qmimodem_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_data *ldd = ofono_lte_get_data(lte);
struct cb_data *cbd = cb_data_new(cb, data);
struct qmi_param* param;
struct {
uint8_t type;
uint8_t index;
} __attribute__((packed)) p = {
.type = 0, /* 3GPP */
};
DBG("");
p.index = ldd->default_profile;
param = qmi_param_new();
if (!param)
goto error;
/* Profile selector */
qmi_param_append(param, 0x01, sizeof(p), &p);
/* WDS APN Name */
qmi_param_append(param, QMI_WDS_PARAM_APN,
strlen(info->apn), info->apn);
/* Modify profile */
if (qmi_service_send(ldd->wds, 0x28, param,
modify_profile_cb, cbd, g_free) > 0)
return;
qmi_param_free(param);
error:
CALLBACK_WITH_FAILURE(cb, cbd->data);
}
static void reset_profile_cb(struct qmi_result *result, void *user_data)
{
struct ofono_lte *lte = user_data;
uint16_t error;
DBG("");
if (qmi_result_set_error(result, &error))
ofono_error("Reset profile error: %hd", error);
ofono_lte_register(lte);
}
static void get_default_profile_cb(struct qmi_result *result, void *user_data)
{
struct ofono_lte *lte = user_data;
struct lte_data *ldd = ofono_lte_get_data(lte);
uint16_t error;
uint8_t index;
struct qmi_param *param;
struct {
uint8_t type;
uint8_t index;
} __attribute__((packed)) p = {
.type = 0, /* 3GPP */
};
DBG("");
if (qmi_result_set_error(result, &error)) {
ofono_error("Get default profile error: %hd", error);
goto error;
}
/* Profile index */
if (!qmi_result_get_uint8(result, 0x01, &index)) {
ofono_error("Failed query default profile");
goto error;
}
DBG("Default profile index: %hhd", index);
ldd->default_profile = index;
p.index = index;
param = qmi_param_new();
if (!param)
goto error;
/* Profile selector */
qmi_param_append(param, 0x01, sizeof(p), &p);
/* Reset profile */
if (qmi_service_send(ldd->wds, 0x4b, param,
reset_profile_cb, lte, NULL) > 0)
return;
qmi_param_free(param);
error:
ofono_error("Failed to reset profile %hhd", index);
ofono_lte_remove(lte);
}
static void create_wds_cb(struct qmi_service *service, void *user_data)
{
struct ofono_lte *lte = user_data;
struct lte_data *ldd = ofono_lte_get_data(lte);
struct qmi_param *param;
struct {
uint8_t type;
uint8_t family;
} __attribute((packed)) p = {
.type = 0, /* 3GPP */
.family = 0, /* embedded */
};
DBG("");
if (!service) {
ofono_error("Failed to request WDS service");
ofono_lte_remove(lte);
return;
}
ldd->wds = qmi_service_ref(service);
/* Query the default profile */
param = qmi_param_new();
if (!param)
goto error;
/* Profile type */
qmi_param_append(param, 0x1, sizeof(p), &p);
/* Get default profile */
if (qmi_service_send(ldd->wds, 0x49, param,
get_default_profile_cb, lte, NULL) > 0)
return;
qmi_param_free(param);
error:
ofono_error("Failed to query default profile");
ofono_lte_register(lte);
}
static int qmimodem_lte_probe(struct ofono_lte *lte, void *data)
{
struct qmi_device *device = data;
struct lte_data *ldd;
DBG("qmimodem lte probe");
ldd = g_try_new0(struct lte_data, 1);
if (!ldd)
return -ENOMEM;
ofono_lte_set_data(lte, ldd);
qmi_service_create_shared(device, QMI_SERVICE_WDS,
create_wds_cb, lte, NULL);
return 0;
}
static void qmimodem_lte_remove(struct ofono_lte *lte)
{
struct lte_data *ldd = ofono_lte_get_data(lte);
DBG("");
ofono_lte_set_data(lte, NULL);
qmi_service_unregister_all(ldd->wds);
qmi_service_unref(ldd->wds);
g_free(ldd);
}
static struct ofono_lte_driver driver = {
.name = "qmimodem",
.probe = qmimodem_lte_probe,
.remove = qmimodem_lte_remove,
.set_default_attach_info = qmimodem_lte_set_default_attach_info,
};
void qmi_lte_init(void)
{
ofono_lte_driver_register(&driver);
}
void qmi_lte_exit(void)
{
ofono_lte_driver_unregister(&driver);
}

View File

@@ -39,6 +39,7 @@ static int qmimodem_init(void)
qmi_ussd_init();
qmi_gprs_init();
qmi_gprs_context_init();
qmi_lte_init();
qmi_radio_settings_init();
qmi_location_reporting_init();
qmi_netmon_init();
@@ -51,6 +52,7 @@ static void qmimodem_exit(void)
qmi_netmon_exit();
qmi_location_reporting_exit();
qmi_radio_settings_exit();
qmi_lte_exit();
qmi_gprs_context_exit();
qmi_gprs_exit();
qmi_ussd_exit();

View File

@@ -48,6 +48,9 @@ extern void qmi_gprs_exit(void);
extern void qmi_gprs_context_init(void);
extern void qmi_gprs_context_exit(void);
extern void qmi_lte_init(void);
extern void qmi_lte_exit(void);
extern void qmi_radio_settings_init(void);
extern void qmi_radio_settings_exit(void);

View File

@@ -277,6 +277,9 @@ static void qmi_radio_settings_remove(struct ofono_radio_settings *rs)
ofono_radio_settings_set_data(rs, NULL);
qmi_service_unregister_all(data->dms);
qmi_service_unref(data->dms);
qmi_service_unregister_all(data->nas);
qmi_service_unref(data->nas);

View File

@@ -1,7 +1,8 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2018 Jolla Ltd.
* Copyright (C) 2015-2019 Jolla Ltd.
* Copyright (C) 2019 Open Mobile Platform LLC.
*
* 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
@@ -237,337 +238,6 @@ char *ril_config_ints_to_string(GUtilInts *ints, char separator)
return NULL;
}
/**
* The ril_config_merge_files() function does the following:
*
* 1. Loads the specified key file (say, "/etc/foo.conf")
* 2. Scans the subdirectory named after the file (e.g. "/etc/foo.d/")
* for the files with the same suffix as the main file (e.g. "*.conf")
* 3. Sorts the files from the subdirectory (alphabetically)
* 4. Merges the contents of the additional files with the main file
* according to their sort order.
*
* When the entries are merged, keys and groups overwrite the exising
* ones by default. Keys can be suffixed with special characters to
* remove or modify the existing entries instead:
*
* ':' Sets the (default) value if the key is missing
* '+' Appends values to the string list
* '?' Appends only new (non-existent) values to the string list
* '-' Removes the values from the string list
*
* Both keys and groups can be prefixed with '!' to remove the entire key
* or group.
*
* For example if we merge these two files:
*
* /etc/foo.conf:
*
* [foo]
* a=1
* b=2,3
* c=4
* d=5
* [bar]
* e=5
*
* /etc/foo.d/bar.conf:
*
* [foo]
* a+=2
* b-=2
* c=5
* !d
* [!bar]
*
* we end up with this:
*
* [foo]
* a=1
* b=2,3
* c=5
*
* Not that the list separator is assumed to be ',' (rather than default ';').
* The keyfile passed to ril_config_merge_files() should use the same list
* separator, because the default values are copied from the config files
* as is.
*/
static gint ril_config_sort_files(gconstpointer a, gconstpointer b)
{
/* The comparison function for g_ptr_array_sort() doesn't take
* the pointers from the array as arguments, it takes pointers
* to the pointers in the array. */
return strcmp(*(char**)a, *(char**)b);
}
static char **ril_config_collect_files(const char *path, const char *suffix)
{
/* Returns sorted list of regular files in the directory,
* optionally having the specified suffix (e.g. ".conf").
* Returns NULL if nothing appropriate has been found. */
char **files = NULL;
DIR *d = opendir(path);
if (d) {
GPtrArray *list = g_ptr_array_new();
const struct dirent *p;
while ((p = readdir(d)) != NULL) {
/* No need to even stat . and .. */
if (strcmp(p->d_name, ".") &&
strcmp(p->d_name, "..") && (!suffix ||
g_str_has_suffix(p->d_name, suffix))) {
struct stat st;
char *buf = g_strconcat(path, "/", p->d_name,
NULL);
if (!stat(buf, &st) && S_ISREG(st.st_mode)) {
g_ptr_array_add(list, buf);
} else {
g_free(buf);
}
}
}
if (list->len > 0) {
g_ptr_array_sort(list, ril_config_sort_files);
g_ptr_array_add(list, NULL);
files = (char**)g_ptr_array_free(list, FALSE);
} else {
g_ptr_array_free(list, TRUE);
}
closedir(d);
}
return files;
}
static int ril_config_list_find(char **list, gsize len, const char *value)
{
guint i;
for (i = 0; i < len; i++) {
if (!strcmp(list[i], value)) {
return i;
}
}
return -1;
}
static void ril_config_list_append(GKeyFile *conf, GKeyFile *k,
const char *group, const char *key,
char **values, gsize n, gboolean unique)
{
/* Note: will steal strings from values */
if (n > 0) {
int i;
gsize len = 0;
gchar **list = g_key_file_get_string_list(conf, group, key,
&len, NULL);
GPtrArray *newlist = g_ptr_array_new_full(0, g_free);
for (i = 0; i < (int)len; i++) {
g_ptr_array_add(newlist, list[i]);
}
for (i = 0; i < (int)n; i++) {
char *val = values[i];
if (!unique || ril_config_list_find((char**)
newlist->pdata, newlist->len, val) < 0) {
/* Move the string to the new list */
g_ptr_array_add(newlist, val);
memmove(values + i, values + i + 1,
sizeof(char*) * (n - i));
i--;
n--;
}
}
if (newlist->len > len) {
g_key_file_set_string_list(conf, group, key,
(const gchar * const *) newlist->pdata,
newlist->len);
}
/* Strings are deallocated by GPtrArray */
g_ptr_array_free(newlist, TRUE);
g_free(list);
}
}
static void ril_config_list_remove(GKeyFile *conf, GKeyFile *k,
const char *group, const char *key, char **values, gsize n)
{
if (n > 0) {
gsize len = 0;
gchar **list = g_key_file_get_string_list(conf, group, key,
&len, NULL);
if (len > 0) {
gsize i;
const gsize oldlen = len;
for (i = 0; i < n; i++) {
int pos;
/* Remove all matching values */
while ((pos = ril_config_list_find(list, len,
values[i])) >= 0) {
g_free(list[pos]);
memmove(list + pos, list + pos + 1,
sizeof(char*) * (len - pos));
len--;
}
}
if (len < oldlen) {
g_key_file_set_string_list(conf, group, key,
(const gchar * const *) list, len);
}
}
g_strfreev(list);
}
}
static void ril_config_merge_group(GKeyFile *conf, GKeyFile *k,
const char *group)
{
gsize i, n = 0;
char **keys = g_key_file_get_keys(k, group, &n, NULL);
for (i=0; i<n; i++) {
char *key = keys[i];
if (key[0] == '!') {
if (key[1]) {
g_key_file_remove_key(conf, group, key+1, NULL);
}
} else {
const gsize len = strlen(key);
const char last = (len > 0) ? key[len-1] : 0;
if (last == '+' || last == '?') {
gsize count = 0;
gchar **values = g_key_file_get_string_list(k,
group, key, &count, NULL);
key[len-1] = 0;
ril_config_list_append(conf, k, group, key,
values, count, last == '?');
g_strfreev(values);
} else if (last == '-') {
gsize count = 0;
gchar **values = g_key_file_get_string_list(k,
group, key, &count, NULL);
key[len-1] = 0;
ril_config_list_remove(conf, k, group, key,
values, count);
g_strfreev(values);
} else {
/* Overwrite the value (it must exist in k) */
gchar *value = g_key_file_get_value(k, group,
key, NULL);
if (last == ':') {
/* Default value */
key[len-1] = 0;
if (!g_key_file_has_key(conf,
group, key, NULL)) {
g_key_file_set_value(conf,
group, key, value);
}
} else {
g_key_file_set_value(conf, group, key,
value);
}
g_free(value);
}
}
}
g_strfreev(keys);
}
static void ril_config_merge_keyfile(GKeyFile *conf, GKeyFile *k)
{
gsize i, n = 0;
char **groups = g_key_file_get_groups(k, &n);
for (i=0; i<n; i++) {
const char *group = groups[i];
if (group[0] == '!') {
g_key_file_remove_group(conf, group + 1, NULL);
} else {
ril_config_merge_group(conf, k, group);
}
}
g_strfreev(groups);
}
static void ril_config_merge_file(GKeyFile *conf, const char *file)
{
GKeyFile *k = g_key_file_new();
g_key_file_set_list_separator(k, ',');
if (g_key_file_load_from_file(k, file, 0, NULL)) {
ril_config_merge_keyfile(conf, k);
}
g_key_file_unref(k);
}
void ril_config_merge_files(GKeyFile *conf, const char *file)
{
if (conf && file && file[0]) {
char *dot = strrchr(file, '.');
const char *suffix;
char *dir;
char **files;
if (!dot) {
dir = g_strconcat(file, ".d", NULL);
suffix = NULL;
} else if (!dot[1]) {
dir = g_strconcat(file, "d", NULL);
suffix = NULL;
} else {
/* 2 bytes for ".d" and 1 for NULL terminator */
dir = g_malloc(dot - file + 3);
strncpy(dir, file, dot - file);
strcpy(dir + (dot - file), ".d");
suffix = dot + 1;
}
files = ril_config_collect_files(dir, suffix);
g_free(dir);
/* Load the main config */
if (g_file_test(file, G_FILE_TEST_EXISTS)) {
DBG("Loading %s", file);
ril_config_merge_file(conf, file);
}
if (files) {
char **ptr;
for (ptr = files; *ptr; ptr++) {
DBG("Merging %s", *ptr);
ril_config_merge_file(conf, *ptr);
}
g_strfreev(files);
}
}
}
/*
* Local Variables:
* mode: C

View File

@@ -1,7 +1,8 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2018 Jolla Ltd.
* Copyright (C) 2015-2019 Jolla Ltd.
* Copyright (C) 2019 Open Mobile Platform LLC.
*
* 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
@@ -22,8 +23,6 @@
#define RILCONF_SETTINGS_GROUP "Settings"
void ril_config_merge_files(GKeyFile *conf, const char *file);
char *ril_config_get_string(GKeyFile *file, const char *group,
const char *key);
char **ril_config_get_strings(GKeyFile *file, const char *group,

View File

@@ -1,6 +1,6 @@
/*
* Copyright (C) 2013 Canonical Ltd.
* Copyright (C) 2013-2019 Jolla Ltd.
* Copyright (C) 2013-2020 Jolla Ltd.
*
* 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
@@ -205,10 +205,44 @@ enum ril_data_call_fail_cause {
PDP_FAIL_SERVICE_OPTION_OUT_OF_ORDER = 0x22,
PDP_FAIL_NSAPI_IN_USE = 0x23,
PDP_FAIL_REGULAR_DEACTIVATION = 0x24,
PDP_FAIL_QOS_NOT_ACCEPTED = 0x25,
PDP_FAIL_NETWORK_FAILURE = 0x26,
PDP_FAIL_UMTS_REACTIVATION_REQ = 0x27,
PDP_FAIL_FEATURE_NOT_SUPP = 0x28,
PDP_FAIL_TFT_SEMANTIC_ERROR = 0x29,
PDP_FAIL_TFT_SYTAX_ERROR = 0x2A,
PDP_FAIL_UNKNOWN_PDP_CONTEXT = 0x2B,
PDP_FAIL_FILTER_SEMANTIC_ERROR = 0x2C,
PDP_FAIL_FILTER_SYTAX_ERROR = 0x2D,
PDP_FAIL_PDP_WITHOUT_ACTIVE_TFT = 0x2E,
PDP_FAIL_ONLY_IPV4_ALLOWED = 0x32,
PDP_FAIL_ONLY_IPV6_ALLOWED = 0x33,
PDP_FAIL_ONLY_SINGLE_BEARER_ALLOWED = 0x34,
PDP_FAIL_ESM_INFO_NOT_RECEIVED = 0x35,
PDP_FAIL_PDN_CONN_DOES_NOT_EXIST = 0x36,
PDP_FAIL_MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED = 0x37,
PDP_FAIL_MAX_ACTIVE_PDP_CONTEXT_REACHED = 0x41,
PDP_FAIL_UNSUPPORTED_APN_IN_CURRENT_PLMN = 0x42,
PDP_FAIL_INVALID_TRANSACTION_ID = 0x51,
PDP_FAIL_MESSAGE_INCORRECT_SEMANTIC = 0x5F,
PDP_FAIL_INVALID_MANDATORY_INFO = 0x60,
PDP_FAIL_MESSAGE_TYPE_UNSUPPORTED = 0x61,
PDP_FAIL_MSG_TYPE_NONCOMPATIBLE_STATE = 0x62,
PDP_FAIL_UNKNOWN_INFO_ELEMENT = 0x63,
PDP_FAIL_CONDITIONAL_IE_ERROR = 0x64,
PDP_FAIL_MSG_AND_PROTOCOL_STATE_UNCOMPATIBLE = 0x65,
PDP_FAIL_PROTOCOL_ERRORS = 0x6F,
PDP_FAIL_APN_TYPE_CONFLICT = 0x70,
PDP_FAIL_INVALID_PCSCF_ADDR = 0x71,
PDP_FAIL_INTERNAL_CALL_PREEMPT_BY_HIGH_PRIO_APN = 0x72,
PDP_FAIL_EMM_ACCESS_BARRED = 0x73,
PDP_FAIL_EMERGENCY_IFACE_ONLY = 0x74,
PDP_FAIL_IFACE_MISMATCH = 0x75,
PDP_FAIL_COMPANION_IFACE_IN_USE = 0x76,
PDP_FAIL_IP_ADDRESS_MISMATCH = 0x77,
PDP_FAIL_IFACE_AND_POL_FAMILY_MISMATCH = 0x78,
PDP_FAIL_EMM_ACCESS_BARRED_INFINITE_RETRY = 0x79,
PDP_FAIL_AUTH_FAILURE_ON_EMERGENCY_CALL = 0x7A,
PDP_FAIL_VOICE_REGISTRATION_FAIL = -1,
PDP_FAIL_DATA_REGISTRATION_FAIL = -2,
PDP_FAIL_SIGNAL_LOST = -3,

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2016-2019 Jolla Ltd.
* Copyright (C) 2016-2020 Jolla Ltd.
* Copyright (C) 2019 Open Mobile Platform LLC.
*
* This program is free software; you can redistribute it and/or modify
@@ -14,6 +14,8 @@
* GNU General Public License for more details.
*/
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#include "ril_data.h"
#include "ril_radio.h"
#include "ril_network.h"
@@ -29,6 +31,8 @@
#include <grilio_parser.h>
#include <grilio_request.h>
#include "common.h" /* ACCESS_TECHNOLOGY_EUTRAN */
/* Yes, it does sometimes take minutes in roaming */
#define SETUP_DATA_CALL_TIMEOUT (300*1000) /* ms */
@@ -115,6 +119,7 @@ struct ril_data_priv {
gulong io_event_id[IO_EVENT_COUNT];
gulong settings_event_id[SETTINGS_EVENT_COUNT];
GHashTable* grab;
gboolean downgraded_tech; /* Status 55 workaround */
};
enum ril_data_signal {
@@ -185,7 +190,6 @@ struct ril_data_request_allow_data {
gboolean allow;
};
static void ril_data_manager_check_data(struct ril_data_manager *dm);
static void ril_data_manager_check_network_mode(struct ril_data_manager *dm);
static void ril_data_call_deact_cid(struct ril_data *data, int cid);
static void ril_data_power_update(struct ril_data *self);
@@ -435,7 +439,7 @@ static gboolean ril_data_call_list_equal(const struct ril_data_call_list *l1,
if (!l1 && !l2) {
return TRUE;
} else if (l1 && l2) {
if (l1->version == l1->version && l1->num == l2->num) {
if (l1->version == l2->version && l1->num == l2->num) {
GSList *p1 = l1->calls;
GSList *p2 = l2->calls;
@@ -816,6 +820,31 @@ static gboolean ril_data_call_setup_retry(void *user_data)
return G_SOURCE_REMOVE;
}
static gboolean ril_data_call_retry(struct ril_data_request_setup *setup)
{
struct ril_data_request *req = &setup->req;
const struct ril_data_options *options = &req->data->priv->options;
if (setup->retry_count < options->data_call_retry_limit) {
req->pending_id = 0;
GASSERT(!setup->retry_delay_id);
if (!setup->retry_count) {
/* No delay first time */
setup->retry_count++;
DBG("silent retry %u out of %u", setup->retry_count,
options->data_call_retry_limit);
req->submit(req);
} else {
const guint ms = options->data_call_retry_delay_ms;
DBG("silent retry scheduled in %u ms", ms);
setup->retry_delay_id = g_timeout_add(ms,
ril_data_call_setup_retry, setup);
}
return TRUE;
}
return FALSE;
}
static void ril_data_call_setup_cb(GRilIoChannel *io, int ril_status,
const void *data, guint len, void *user_data)
{
@@ -840,33 +869,49 @@ static void ril_data_call_setup_cb(GRilIoChannel *io, int ril_status,
}
}
if (call && call->status == PDP_FAIL_ERROR_UNSPECIFIED &&
setup->retry_count < priv->options.data_call_retry_limit) {
if (call) {
switch (call->status) {
/*
* According to the comment from ril.h we should silently
* retry. First time we retry immediately and if that doedsn't
* retry. First time we retry immediately and if that doesn't
* work, then after certain delay.
*/
req->pending_id = 0;
GASSERT(!setup->retry_delay_id);
if (!setup->retry_count) {
setup->retry_count++;
DBG("silent retry %u out of %u", setup->retry_count,
priv->options.data_call_retry_limit);
req->submit(req);
} else {
guint ms = priv->options.data_call_retry_delay_ms;
DBG("silent retry scheduled in %u ms", ms);
setup->retry_delay_id = g_timeout_add(ms,
ril_data_call_setup_retry, setup);
case PDP_FAIL_ERROR_UNSPECIFIED:
if (ril_data_call_retry(setup)) {
ril_data_call_list_free(list);
return;
}
break;
/*
* With some networks we sometimes start getting error 55
* (Multiple PDN connections for a given APN not allowed)
* when trying to setup an LTE data call and this error
* doesn't go away until we successfully establish a data
* call over 3G. Then we can switch back to LTE.
*/
case PDP_FAIL_MULTI_CONN_TO_SAME_PDN_NOT_ALLOWED:
if (priv->network->data.access_tech ==
ACCESS_TECHNOLOGY_EUTRAN &&
!priv->downgraded_tech) {
DBG("downgrading preferred technology");
priv->downgraded_tech = TRUE;
ril_data_manager_check_network_mode(priv->dm);
/* And let this call fail */
}
break;
default:
break;
}
ril_data_call_list_free(list);
return;
}
ril_data_request_completed(req);
if (call && call->status == PDP_FAIL_NONE) {
if (priv->downgraded_tech) {
DBG("done with status 55 workaround");
priv->downgraded_tech = FALSE;
ril_data_manager_check_network_mode(priv->dm);
}
if (ril_data_call_list_move_calls(self->data_calls, list) > 0) {
DBG("data call(s) added");
ril_data_signal_emit(self, SIGNAL_CALLS_CHANGED);
@@ -1152,6 +1197,11 @@ static struct ril_data_request *ril_data_allow_new(struct ril_data *data,
/*==========================================================================*
* ril_data
*==========================================================================*/
static enum ofono_radio_access_mode ril_data_max_mode(struct ril_data *self)
{
return self->priv->downgraded_tech ? OFONO_RADIO_ACCESS_MODE_UMTS :
OFONO_RADIO_ACCESS_MODE_ANY;
}
gulong ril_data_add_allow_changed_handler(struct ril_data *self,
ril_data_cb_t cb, void *arg)
@@ -1685,7 +1735,7 @@ static void ril_data_manager_check_network_mode(struct ril_data_manager *self)
ril_network_set_max_pref_mode(network,
(network == lte_network) ?
OFONO_RADIO_ACCESS_MODE_ANY :
ril_data_max_mode(data) :
OFONO_RADIO_ACCESS_MODE_GSM,
FALSE);
}
@@ -1695,7 +1745,7 @@ static void ril_data_manager_check_network_mode(struct ril_data_manager *self)
for (l= self->data_list; l; l = l->next) {
struct ril_data *data = l->data;
ril_network_set_max_pref_mode(data->priv->network,
OFONO_RADIO_ACCESS_MODE_ANY, FALSE);
ril_data_max_mode(data), FALSE);
}
}
}
@@ -1724,7 +1774,7 @@ static void ril_data_manager_switch_data_on(struct ril_data_manager *self,
if (ril_data_manager_handover(self)) {
ril_network_set_max_pref_mode(priv->network,
OFONO_RADIO_ACCESS_MODE_ANY, TRUE);
ril_data_max_mode(data), TRUE);
}
if (priv->options.allow_data == RIL_ALLOW_DATA_ENABLED) {
@@ -1737,7 +1787,7 @@ static void ril_data_manager_switch_data_on(struct ril_data_manager *self,
}
}
static void ril_data_manager_check_data(struct ril_data_manager *self)
void ril_data_manager_check_data(struct ril_data_manager *self)
{
/*
* Don't do anything if there any requests pending.

View File

@@ -89,6 +89,7 @@ struct ril_data_manager;
struct ril_data_manager *ril_data_manager_new(enum ril_data_manager_flags flg);
struct ril_data_manager *ril_data_manager_ref(struct ril_data_manager *dm);
void ril_data_manager_unref(struct ril_data_manager *dm);
void ril_data_manager_check_data(struct ril_data_manager *dm);
void ril_data_manager_assert_data_on(struct ril_data_manager *dm);
typedef void (*ril_data_cb_t)(struct ril_data *data, void *arg);

View File

@@ -1,7 +1,8 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2016 Jolla Ltd.
* Copyright (C) 2016-2020 Jolla Ltd.
* Copyright (C) 2019 Open Mobile Platform LLC.
*
* 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
@@ -13,6 +14,8 @@
* GNU General Public License for more details.
*/
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#include "ril_ecclist.h"
#include "ril_log.h"
@@ -47,6 +50,53 @@ G_DEFINE_TYPE(RilEccList, ril_ecclist, G_TYPE_OBJECT)
#define RIL_ECCLIST(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\
RIL_ECCLIST_TYPE, RilEccList))
static char **ril_ecclist_parse(const char *content)
{
char **ptr;
char **list = NULL;
guint i;
/*
* Some MediaTek devices use ECC,CAT;ECC,CAT kind of syntax
*/
if (strchr(content, ';')) {
list = g_strsplit(content, ";", 0);
for (ptr = list; *ptr; ptr++) {
char* comma;
*ptr = g_strstrip(*ptr);
/* Strip service category */
comma = strchr(*ptr, ',');
if (comma) {
*comma = 0;
}
}
} else {
/* The right ECC,ECC syntax is handled here */
list = g_strsplit(content, ",", 0);
for (ptr = list; *ptr; ptr++) {
*ptr = g_strstrip(*ptr);
}
}
/* Sort the list */
gutil_strv_sort(list, TRUE);
/* Remove empty strings (those are at the beginning after sorting) */
while (list[0] && !list[0][0]) {
list = gutil_strv_remove_at(list, 0, TRUE);
}
/* Remove duplicates */
for (i = 0; list[i] && list[i+1]; i++) {
while (list[i+1] && !strcmp(list[i], list[i+1])) {
list = gutil_strv_remove_at(list, i+1, TRUE);
}
}
return list;
}
static char **ril_ecclist_read(struct ril_ecclist *self)
{
struct ril_ecclist_priv *priv = self->priv;
@@ -58,16 +108,9 @@ static char **ril_ecclist_read(struct ril_ecclist *self)
GError *error = NULL;
if (g_file_get_contents(priv->path, &content, &len, &error)) {
char **ptr;
DBG("%s = %s", priv->name, content);
list = g_strsplit(content, ",", 0);
for (ptr = list; *ptr; ptr++) {
*ptr = g_strstrip(*ptr);
}
gutil_strv_sort(list, TRUE);
} else if (error) {
list = ril_ecclist_parse(content);
} else {
DBG("%s: %s", priv->path, GERRMSG(error));
g_error_free(error);
}
@@ -89,7 +132,8 @@ static void ril_ecclist_update(struct ril_ecclist *self)
DBG("%s changed", priv->name);
g_strfreev(self->list);
self->list = list;
g_signal_emit(self, ril_ecclist_signals[SIGNAL_LIST_CHANGED], 0);
g_signal_emit(self, ril_ecclist_signals
[SIGNAL_LIST_CHANGED], 0);
} else {
g_strfreev(list);
}
@@ -121,10 +165,9 @@ static void ril_ecclist_dir_changed(GUtilInotifyWatch *watch, guint mask,
priv->file_watch = gutil_inotify_watch_callback_new(priv->path,
IN_MODIFY | IN_CLOSE_WRITE,
ril_ecclist_changed, self);
if (priv->file_watch) {
DBG("watching %s", priv->path);
ril_ecclist_update(self);
}
DBG("%swatching %s", priv->file_watch ? "" : "not ",
priv->path);
ril_ecclist_update(self);
}
if (mask & IN_IGNORED) {

View File

@@ -20,6 +20,7 @@
#include "ril_sim_card.h"
#include "ril_sim_settings.h"
#include "ril_cell_info.h"
#include "ril_vendor.h"
#include "ril_data.h"
#include "ril_util.h"
#include "ril_log.h"
@@ -435,6 +436,7 @@ static void ril_modem_remove(struct ofono_modem *ofono)
g_source_remove(md->set_offline.timeout_id);
}
ril_vendor_unref(modem->vendor);
ril_network_unref(modem->network);
ril_sim_card_unref(modem->sim_card);
ril_data_unref(modem->data);
@@ -456,7 +458,7 @@ struct ril_modem *ril_modem_create(GRilIoChannel *io, const char *log_prefix,
const char *ecclist_file, const struct ril_slot_config *config,
struct ril_radio *radio, struct ril_network *network,
struct ril_sim_card *card, struct ril_data *data,
struct ril_sim_settings *settings,
struct ril_sim_settings *settings, struct ril_vendor *vendor,
struct sailfish_cell_info *cell_info)
{
/* Skip the slash from the path, it looks like "/ril_0" */
@@ -483,6 +485,7 @@ struct ril_modem *ril_modem_create(GRilIoChannel *io, const char *log_prefix,
g_strconcat(log_prefix, " ", NULL) : g_strdup("");
modem->ofono = ofono;
modem->vendor = ril_vendor_ref(vendor);
modem->radio = ril_radio_ref(radio);
modem->network = ril_network_ref(network);
modem->sim_card = ril_sim_card_ref(card);

View File

@@ -1,7 +1,8 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2019 Jolla Ltd.
* Copyright (C) 2015-2020 Jolla Ltd.
* Copyright (C) 2019-2020 Open Mobile Platform LLC.
*
* 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
@@ -16,6 +17,7 @@
#include "ril_plugin.h"
#include "ril_network.h"
#include "ril_util.h"
#include "ril_vendor.h"
#include "ril_log.h"
#include "common.h"
@@ -40,8 +42,11 @@ struct ril_netreg {
GRilIoChannel *io;
GRilIoQueue *q;
gboolean network_selection_manual_0;
int signal_strength_dbm_weak;
int signal_strength_dbm_strong;
struct ofono_netreg *netreg;
struct ril_network *network;
struct ril_vendor *vendor;
char *log_prefix;
guint timer_id;
guint notify_id;
@@ -331,100 +336,92 @@ static void ril_netreg_register_manual(struct ofono_netreg *netreg,
grilio_request_unref(req);
}
static int ril_netreg_dbm_to_percentage(int dbm)
static int ril_netreg_qdbm_to_percentage(struct ril_netreg *nd, int qdbm)
{
const int min_dbm = -100; /* very weak signal, 0.0000000001 mW */
const int max_dbm = -60; /* strong signal, 0.000001 mW */
const int min_qdbm = 4 * nd->signal_strength_dbm_weak; /* 4*dBm */
const int max_qdbm = 4 * nd->signal_strength_dbm_strong; /* 4*dBm */
return (dbm <= min_dbm) ? 1 :
(dbm >= max_dbm) ? 100 :
(100 * (dbm - min_dbm) / (max_dbm - min_dbm));
return (qdbm <= min_qdbm) ? 1 :
(qdbm >= max_qdbm) ? 100 :
(100 * (qdbm - min_qdbm) / (max_qdbm - min_qdbm));
}
static int ril_netreg_get_signal_strength(const void *data, guint len)
static int ril_netreg_get_signal_strength(struct ril_netreg *nd,
const void *data, guint len)
{
GRilIoParser rilp;
int gw_signal = 0, cdma_dbm = 0, evdo_dbm = 0, lte_signal = 0;
int rsrp = 0, tdscdma_dbm = 0;
struct ril_vendor_signal_strength signal;
grilio_parser_init(&rilp, data, len);
signal.gsm = INT_MAX;
signal.lte = INT_MAX;
signal.qdbm = 0;
/* GW_SignalStrength */
grilio_parser_get_int32(&rilp, &gw_signal);
grilio_parser_get_int32(&rilp, NULL); /* bitErrorRate */
if (!ril_vendor_signal_strength_parse(nd->vendor, &signal, &rilp)) {
gint32 rsrp = 0, tdscdma_dbm = 0;
/* CDMA_SignalStrength */
grilio_parser_get_int32(&rilp, &cdma_dbm);
grilio_parser_get_int32(&rilp, NULL); /* ecio */
/* Apply default parsing algorithm */
grilio_parser_init(&rilp, data, len);
signal.gsm = INT_MAX;
signal.lte = INT_MAX;
signal.qdbm = 0;
/* EVDO_SignalStrength */
grilio_parser_get_int32(&rilp, &evdo_dbm);
grilio_parser_get_int32(&rilp, NULL); /* ecio */
grilio_parser_get_int32(&rilp, NULL); /* signalNoiseRatio */
/* GW_SignalStrength */
grilio_parser_get_int32(&rilp, &signal.gsm);
grilio_parser_get_int32(&rilp, NULL); /* bitErrorRate */
/* LTE_SignalStrength */
grilio_parser_get_int32(&rilp, &lte_signal);
grilio_parser_get_int32(&rilp, &rsrp);
/* CDMA_SignalStrength */
grilio_parser_get_int32(&rilp, NULL); /* dbm */
grilio_parser_get_int32(&rilp, NULL); /* ecio */
/* Skip the rest of LTE_SignalStrength_v8 */
if (grilio_parser_get_int32(&rilp, NULL) && /* rsrq */
grilio_parser_get_int32(&rilp, NULL) && /* rssnr */
grilio_parser_get_int32(&rilp, NULL) && /* cqi */
grilio_parser_get_int32(&rilp, NULL)) { /* timingAdvance */
/* EVDO_SignalStrength */
grilio_parser_get_int32(&rilp, NULL); /* dbm */
grilio_parser_get_int32(&rilp, NULL); /* ecio */
grilio_parser_get_int32(&rilp, NULL); /* signalNoiseRatio */
/* TD_SCDMA_SignalStrength */
grilio_parser_get_int32(&rilp, &tdscdma_dbm); /* rscp */
/* LTE_SignalStrength */
grilio_parser_get_int32(&rilp, &signal.lte);
grilio_parser_get_int32(&rilp, &rsrp);
/* The rest is considered optional */
if (grilio_parser_get_int32(&rilp, NULL) && /* rsrq */
grilio_parser_get_int32(&rilp, NULL) && /* rssnr */
grilio_parser_get_int32(&rilp, NULL) && /* cqi */
grilio_parser_get_int32(&rilp, NULL) && /* timingAdv */
/* TD_SCDMA_SignalStrength */
grilio_parser_get_int32(&rilp, &tdscdma_dbm) &&
/* RSCP range: 25 to 120 dBm per 3GPP TS 25.123 */
tdscdma_dbm >= 25 && tdscdma_dbm <= 120) {
signal.qdbm = -4 * tdscdma_dbm;
} else if (signal.lte == 99 && rsrp >= 44 && rsrp <= 140) {
/* RSRP range: 44 to 140 dBm per 3GPP TS 36.133 */
signal.qdbm = -rsrp;
}
}
if (rsrp == INT_MAX) {
DBG("gw: %d, cdma: %d, evdo: %d, lte: %d, tdscdma: %d",
gw_signal, cdma_dbm, evdo_dbm,
lte_signal, tdscdma_dbm);
} else {
DBG("gw: %d, cdma: %d, evdo: %d, lte: %d rsrp: %d, tdscdma: %d",
gw_signal, cdma_dbm, evdo_dbm,
lte_signal, rsrp, tdscdma_dbm);
}
DBG("gw: %d, lte: %d, qdbm: %d", signal.gsm, signal.lte, signal.qdbm);
/* Return the first valid one */
/* Some RILs (namely, from MediaTek) report 0 here AND a valid LTE
* RSRP value. If we've got zero, don't report it just yet. */
if (gw_signal >= 1 && gw_signal <= 31) {
if (signal.gsm >= 1 && signal.gsm <= 31) {
/* Valid values are (0-31, 99) as defined in TS 27.007 */
return (gw_signal * 100) / 31;
return (signal.gsm * 100) / 31;
}
/* Valid values are (0-31, 99) as defined in TS 27.007 */
if (lte_signal >= 0 && lte_signal <= 31) {
return (lte_signal * 100) / 31;
if (signal.lte >= 0 && signal.lte <= 31) {
return (signal.lte * 100) / 31;
}
/* RSCP range: 25 to 120 dBm as defined in 3GPP TS 25.123 */
if (tdscdma_dbm >= 25 && tdscdma_dbm <= 120) {
return ril_netreg_dbm_to_percentage(-tdscdma_dbm);
}
/* RSRP range: 44 to 140 dBm as defined in 3GPP TS 36.133 */
if (lte_signal == 99 && rsrp >= 44 && rsrp <= 140) {
return ril_netreg_dbm_to_percentage(-rsrp);
}
/* If we've got zero strength and no valid RSRP, then so be it */
if (gw_signal == 0) {
if (signal.qdbm < 0) {
return ril_netreg_qdbm_to_percentage(nd, signal.qdbm);
} else if (signal.gsm == 0) {
return 0;
} else {
return -1;
}
/* In case of dbm, return the value directly */
if (cdma_dbm != -1) {
return MIN(cdma_dbm, 100);
}
if (evdo_dbm != -1) {
return MIN(evdo_dbm, 100);
}
return -1;
}
static void ril_netreg_strength_notify(GRilIoChannel *io, guint ril_event,
@@ -434,9 +431,11 @@ static void ril_netreg_strength_notify(GRilIoChannel *io, guint ril_event,
int strength;
GASSERT(ril_event == RIL_UNSOL_SIGNAL_STRENGTH);
strength = ril_netreg_get_signal_strength(data, len);
strength = ril_netreg_get_signal_strength(nd, data, len);
DBG_(nd, "%d", strength);
ofono_netreg_strength_notify(nd->netreg, strength);
if (strength >= 0) {
ofono_netreg_strength_notify(nd->netreg, strength);
}
}
static void ril_netreg_strength_cb(GRilIoChannel *io, int status,
@@ -447,8 +446,8 @@ static void ril_netreg_strength_cb(GRilIoChannel *io, int status,
struct ofono_error error;
if (status == RIL_E_SUCCESS) {
int strength = ril_netreg_get_signal_strength(data, len);
cb(ril_error_ok(&error), strength, cbd->data);
cb(ril_error_ok(&error), ril_netreg_get_signal_strength
(cbd->nd, data, len), cbd->data);
} else {
ofono_error("Failed to retrive the signal strength: %s",
ril_error_to_string(status));
@@ -558,9 +557,12 @@ static int ril_netreg_probe(struct ofono_netreg *netreg, unsigned int vendor,
DBG_(nd, "%p", netreg);
nd->io = grilio_channel_ref(ril_modem_io(modem));
nd->q = grilio_queue_new(nd->io);
nd->vendor = ril_vendor_ref(modem->vendor);
nd->network = ril_network_ref(modem->network);
nd->netreg = netreg;
nd->network_selection_manual_0 = config->network_selection_manual_0;
nd->signal_strength_dbm_weak = config->signal_strength_dbm_weak;
nd->signal_strength_dbm_strong = config->signal_strength_dbm_strong;
ofono_netreg_set_data(netreg, nd);
nd->timer_id = g_idle_add(ril_netreg_register, nd);
@@ -589,6 +591,7 @@ static void ril_netreg_remove(struct ofono_netreg *netreg)
ril_network_remove_all_handlers(nd->network, nd->network_event_id);
ril_network_unref(nd->network);
ril_vendor_unref(nd->vendor);
grilio_channel_remove_all_handlers(nd->io, nd->ril_event_id);
grilio_channel_unref(nd->io);

View File

@@ -1,7 +1,8 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2019 Jolla Ltd.
* Copyright (C) 2015-2020 Jolla Ltd.
* Copyright (C) 2019 Open Mobile Platform LLC.
*
* 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
@@ -13,6 +14,8 @@
* GNU General Public License for more details.
*/
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#include "ril_network.h"
#include "ril_radio.h"
#include "ril_sim_card.h"
@@ -110,6 +113,7 @@ struct ril_network_priv {
gboolean set_initial_attach_apn;
struct ofono_network_operator operator;
gboolean assert_rat;
gboolean force_gsm_when_radio_off;
gboolean use_data_profiles;
int mms_data_profile_id;
GSList *data_profiles;
@@ -515,13 +519,12 @@ static enum ofono_radio_access_mode ril_network_actual_pref_mode
struct ril_network_priv *priv = self->priv;
/*
* On dual-SIM phones such as Jolla C only one slot at a time
* is allowed to use LTE. Even if the slot which has been using
* LTE gets powered off, we still need to explicitely set its
* preferred mode to GSM, to make LTE machinery available to
* the other slot. This sort of behaviour might not be necessary
* on some hardware and can (should) be made configurable when
* it becomes necessary.
* On most dual-SIM phones only one slot at a time is allowed
* to use LTE. On some phones (such as Jolla C), even if the
* slot which has been using LTE gets powered off, we still
* need to explicitly set its preferred mode to GSM, to make
* LTE machinery available to the other slot. This behavior is
* configurable.
*/
const enum ofono_radio_access_mode max_pref_mode =
(priv->radio->state == RADIO_STATE_ON) ? self->max_pref_mode :
@@ -902,39 +905,52 @@ static void ril_network_check_pref_mode(struct ril_network *self,
gboolean immediate)
{
struct ril_network_priv *priv = self->priv;
const enum ofono_radio_access_mode expected_mode =
ril_network_actual_pref_mode(self);
const enum ofono_radio_access_mode current_mode =
ril_network_rat_to_mode(priv->rat);
struct ril_radio *radio = priv->radio;
if (priv->timer[TIMER_FORCE_CHECK_PREF_MODE]) {
ril_network_stop_timer(self, TIMER_FORCE_CHECK_PREF_MODE);
/*
* TIMER_FORCE_CHECK_PREF_MODE is scheduled by
* ril_network_pref_mode_changed_cb and is meant
* to force radio tech check right now.
*/
immediate = TRUE;
}
/*
* On most dual-SIM phones only one slot at a time is allowed
* to use LTE. On some phones (such as Jolla C), even if the
* slot which has been using LTE gets powered off, we still
* need to explicitly set its preferred mode to GSM, to make
* LTE machinery available to the other slot. This behavior is
* configurable.
*/
if (radio->state == RADIO_STATE_ON || priv->force_gsm_when_radio_off) {
const enum ofono_radio_access_mode expected =
ril_network_actual_pref_mode(self);
const enum ofono_radio_access_mode actual =
ril_network_rat_to_mode(priv->rat);
if (priv->rat >= 0 && current_mode != expected_mode) {
DBG_(self, "rat %d (%s), expected %s", priv->rat,
ofono_radio_access_mode_to_string(current_mode),
ofono_radio_access_mode_to_string(expected_mode));
}
if (priv->timer[TIMER_FORCE_CHECK_PREF_MODE]) {
ril_network_stop_timer(self,
TIMER_FORCE_CHECK_PREF_MODE);
/*
* TIMER_FORCE_CHECK_PREF_MODE is scheduled by
* ril_network_pref_mode_changed_cb and is meant
* to force radio tech check right now.
*/
immediate = TRUE;
}
if (immediate) {
ril_network_stop_timer(self, TIMER_SET_RAT_HOLDOFF);
}
if (priv->rat >= 0 && actual != expected) {
DBG_(self, "rat %d (%s), expected %s", priv->rat,
ofono_radio_access_mode_to_string(actual),
ofono_radio_access_mode_to_string(expected));
}
if (current_mode != expected_mode || priv->assert_rat) {
const int rat = ril_network_mode_to_rat(self, expected_mode);
if (immediate) {
ril_network_stop_timer(self, TIMER_SET_RAT_HOLDOFF);
}
if (!priv->timer[TIMER_SET_RAT_HOLDOFF]) {
ril_network_set_pref_mode(self, rat);
} else {
/* OK, later */
DBG_(self, "need to set rat mode %d", rat);
if (actual != expected || priv->assert_rat) {
const int rat = ril_network_mode_to_rat(self, expected);
if (!priv->timer[TIMER_SET_RAT_HOLDOFF]) {
ril_network_set_pref_mode(self, rat);
} else {
/* OK, later */
DBG_(self, "need to set rat mode %d", rat);
}
}
}
}
@@ -1228,6 +1244,7 @@ struct ril_network *ril_network_new(const char *path, GRilIoChannel *io,
priv->lte_network_mode = config->lte_network_mode;
priv->umts_network_mode = config->umts_network_mode;
priv->network_mode_timeout = config->network_mode_timeout;
priv->force_gsm_when_radio_off = config->force_gsm_when_radio_off;
priv->use_data_profiles = config->use_data_profiles;
priv->mms_data_profile_id = config->mms_data_profile_id;

View File

@@ -1,8 +1,8 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2019 Jolla Ltd.
* Copyright (C) 2019 Open Mobile Platform LLC.
* Copyright (C) 2015-2020 Jolla Ltd.
* Copyright (C) 2019-2020 Open Mobile Platform LLC.
*
* 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
@@ -28,7 +28,10 @@
#include "ril_devmon.h"
#include "ril_log.h"
#include <ofono/sailfish_manager.h>
#include "ofono.h"
#include "sailfish_manager.h"
#include <ofono/storage.h>
#include <ofono/watch.h>
#include <grilio_transport.h>
@@ -61,7 +64,7 @@
#define RIL_SUB_SIZE 4
#define RILMODEM_CONF_FILE CONFIGDIR "/ril_subscription.conf"
#define RILMODEM_CONF_FILE "ril_subscription.conf"
#define RILMODEM_DEFAULT_IDENTITY "radio:radio"
#define RILMODEM_DEFAULT_SOCK "/dev/socket/rild"
#define RILMODEM_DEFAULT_SOCK2 "/dev/socket/rild2"
@@ -70,6 +73,8 @@
#define RILMODEM_DEFAULT_LTE_MODE PREF_NET_TYPE_LTE_GSM_WCDMA
#define RILMODEM_DEFAULT_UMTS_MODE PREF_NET_TYPE_GSM_WCDMA_AUTO
#define RILMODEM_DEFAULT_NETWORK_MODE_TIMEOUT (20*1000) /* ms */
#define RILMODEM_DEFAULT_DBM_WEAK (-100) /* very weak, 0.0000000001 mW */
#define RILMODEM_DEFAULT_DBM_STRONG (-60) /* strong signal, 0.000001 mW */
#define RILMODEM_DEFAULT_ENABLE_VOICECALL TRUE
#define RILMODEM_DEFAULT_ENABLE_CBS TRUE
#define RILMODEM_DEFAULT_ENABLE_STK TRUE
@@ -89,6 +94,7 @@
#define RILMODEM_DEFAULT_RADIO_POWER_CYCLE TRUE
#define RILMODEM_DEFAULT_CONFIRM_RADIO_POWER_ON TRUE
#define RILMODEM_DEFAULT_NETWORK_SELECTION_MANUAL_0 TRUE
#define RILMODEM_DEFAULT_FORCE_GSM_WHEN_RADIO_OFF TRUE
#define RILMODEM_DEFAULT_USE_DATA_PROFILES FALSE
#define RILMODEM_DEFAULT_MMS_DATA_PROFILE_ID RIL_DATA_PROFILE_IMS
#define RILMODEM_DEFAULT_SLOT_FLAGS SAILFISH_SLOT_NO_FLAGS
@@ -128,6 +134,7 @@
#define RILCONF_LTE_MODE "lteNetworkMode"
#define RILCONF_UMTS_MODE "umtsNetworkMode"
#define RILCONF_NETWORK_MODE_TIMEOUT "networkModeTimeout"
#define RILCONF_SIGNAL_STRENGTH_RANGE "signalStrengthRange"
#define RILCONF_UICC_WORKAROUND "uiccWorkaround"
#define RILCONF_ECCLIST_FILE "ecclistFile"
#define RILCONF_ALLOW_DATA_REQ "allowDataReq"
@@ -143,6 +150,7 @@
#define RILCONF_CONFIRM_RADIO_POWER_ON "confirmRadioPowerOn"
#define RILCONF_SINGLE_DATA_CONTEXT "singleDataContext"
#define RILCONF_NETWORK_SELECTION_MANUAL_0 "networkSelectionManual0"
#define RILCONF_FORCE_GSM_WHEN_RADIO_OFF "forceGsmWhenRadioOff"
#define RILCONF_USE_DATA_PROFILES "useDataProfiles"
#define RILCONF_MMS_DATA_PROFILE_ID "mmsDataProfileId"
#define RILCONF_DEVMON "deviceStateTracking"
@@ -836,7 +844,7 @@ static void ril_plugin_create_modem(ril_slot *slot)
modem = ril_modem_create(slot->io, log_prefix, slot->path, slot->imei,
slot->imeisv, slot->ecclist_file, &slot->config, slot->radio,
slot->network, slot->sim_card, slot->data, slot->sim_settings,
slot->cell_info);
slot->vendor, slot->cell_info);
if (modem) {
slot->modem = modem;
@@ -910,6 +918,7 @@ static void ril_plugin_radio_caps_cb(const struct ril_radio_capability *cap,
static void ril_plugin_manager_started(ril_plugin *plugin)
{
ril_plugin_drop_orphan_slots(plugin);
ril_data_manager_check_data(plugin->data_manager);
sailfish_slot_manager_started(plugin->handle);
}
@@ -1184,6 +1193,9 @@ static ril_slot *ril_plugin_slot_new_take(char *transport,
config->techs = RILMODEM_DEFAULT_TECHS;
config->lte_network_mode = RILMODEM_DEFAULT_LTE_MODE;
config->umts_network_mode = RILMODEM_DEFAULT_UMTS_MODE;
config->network_mode_timeout = RILMODEM_DEFAULT_NETWORK_MODE_TIMEOUT;
config->signal_strength_dbm_weak = RILMODEM_DEFAULT_DBM_WEAK;
config->signal_strength_dbm_strong = RILMODEM_DEFAULT_DBM_STRONG;
config->empty_pin_query = RILMODEM_DEFAULT_EMPTY_PIN_QUERY;
config->radio_power_cycle = RILMODEM_DEFAULT_RADIO_POWER_CYCLE;
config->confirm_radio_power_on =
@@ -1195,6 +1207,8 @@ static ril_slot *ril_plugin_slot_new_take(char *transport,
RILMODEM_DEFAULT_QUERY_AVAILABLE_BAND_MODE;
config->network_selection_manual_0 =
RILMODEM_DEFAULT_NETWORK_SELECTION_MANUAL_0;
config->force_gsm_when_radio_off =
RILMODEM_DEFAULT_FORCE_GSM_WHEN_RADIO_OFF;
config->use_data_profiles = RILMODEM_DEFAULT_USE_DATA_PROFILES;
config->mms_data_profile_id = RILMODEM_DEFAULT_MMS_DATA_PROFILE_ID;
slot->timeout = RILMODEM_DEFAULT_TIMEOUT;
@@ -1231,6 +1245,8 @@ static void ril_plugin_slot_apply_vendor_defaults(ril_slot *slot)
defaults.empty_pin_query = config->empty_pin_query;
defaults.mms_data_profile_id = config->mms_data_profile_id;
defaults.use_data_profiles = config->use_data_profiles;
defaults.force_gsm_when_radio_off =
config->force_gsm_when_radio_off;
defaults.query_available_band_mode =
config->query_available_band_mode;
@@ -1241,6 +1257,8 @@ static void ril_plugin_slot_apply_vendor_defaults(ril_slot *slot)
config->empty_pin_query = defaults.empty_pin_query;
config->use_data_profiles = defaults.use_data_profiles;
config->mms_data_profile_id = defaults.mms_data_profile_id;
config->force_gsm_when_radio_off =
defaults.force_gsm_when_radio_off;
config->query_available_band_mode =
defaults.query_available_band_mode;
}
@@ -1350,6 +1368,7 @@ static ril_slot *ril_plugin_parse_config_group(GKeyFile *file,
char *sval;
char **strv;
char *modem;
GUtilInts *ints;
GHashTable *transport_params = g_hash_table_new_full(g_str_hash,
g_str_equal, g_free, g_free);
char *transport = NULL;
@@ -1469,6 +1488,14 @@ static ril_slot *ril_plugin_parse_config_group(GKeyFile *file,
config->network_selection_manual_0 ? "yes" : "no");
}
/* forceGsmWhenRadioOff */
if (ril_config_get_boolean(file, group,
RILCONF_FORCE_GSM_WHEN_RADIO_OFF,
&config->force_gsm_when_radio_off)) {
DBG("%s: " RILCONF_FORCE_GSM_WHEN_RADIO_OFF " %s", group,
config->force_gsm_when_radio_off ? "yes" : "no");
}
/* useDataProfiles */
if (ril_config_get_boolean(file, group, RILCONF_USE_DATA_PROFILES,
&config->use_data_profiles)) {
@@ -1539,6 +1566,21 @@ static ril_slot *ril_plugin_parse_config_group(GKeyFile *file,
config->network_mode_timeout);
}
/* signalStrengthRange */
ints = ril_config_get_ints(file, group, RILCONF_SIGNAL_STRENGTH_RANGE);
if (gutil_ints_get_count(ints) == 2) {
const int* dbms = gutil_ints_get_data(ints, NULL);
/* MIN,MAX */
if (dbms[0] < dbms[1]) {
DBG("%s: " RILCONF_SIGNAL_STRENGTH_RANGE " [%d,%d]",
group, dbms[0], dbms[1]);
config->signal_strength_dbm_weak = dbms[0];
config->signal_strength_dbm_strong = dbms[1];
}
}
gutil_ints_unref(ints);
/* enable4G (deprecated but still supported) */
ival = config->techs;
if (ril_config_get_flag(file, group, RILCONF_4G,
@@ -1865,7 +1907,7 @@ static GSList *ril_plugin_load_config(const char *path,
GKeyFile *file = g_key_file_new();
gboolean empty = FALSE;
ril_config_merge_files(file, path);
config_merge_files(file, path);
if (ril_config_get_boolean(file, RILCONF_SETTINGS_GROUP,
RILCONF_SETTINGS_EMPTY, &empty) && empty) {
DBG("Empty config");
@@ -2051,12 +2093,15 @@ static guint ril_plugin_manager_start(ril_plugin *plugin)
{
struct ril_plugin_settings *ps = &plugin->settings;
guint start_timeout = 0;
char* config_file = g_build_filename(ofono_config_dir(),
RILMODEM_CONF_FILE, NULL);
DBG("");
GASSERT(!plugin->start_timeout_id);
plugin->slots = ril_plugin_load_config(RILMODEM_CONF_FILE, ps);
plugin->slots = ril_plugin_load_config(config_file, ps);
plugin->data_manager = ril_data_manager_new(ps->dm_flags);
ril_plugin_init_slots(plugin);
g_free(config_file);
ofono_modem_driver_register(&ril_modem_driver);
ofono_sim_driver_register(&ril_sim_driver);

View File

@@ -1,7 +1,8 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2017 Jolla Ltd.
* Copyright (C) 2015-2019 Jolla Ltd.
* Copyright (C) 2019 Open Mobile Platform LLC.
*
* 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
@@ -52,6 +53,7 @@ struct ril_modem {
const char *ecclist_file;
struct ofono_modem *ofono;
struct sailfish_cell_info *cell_info;
struct ril_vendor *vendor;
struct ril_radio *radio;
struct ril_data *data;
struct ril_network *network;
@@ -70,7 +72,7 @@ struct ril_modem *ril_modem_create(GRilIoChannel *io, const char *log_prefix,
const char *ecclist_file, const struct ril_slot_config *config,
struct ril_radio *radio, struct ril_network *network,
struct ril_sim_card *card, struct ril_data *data,
struct ril_sim_settings *settings,
struct ril_sim_settings *settings, struct ril_vendor *vendor,
struct sailfish_cell_info *cell_info);
void ril_modem_delete(struct ril_modem *modem);
struct ofono_sim *ril_modem_ofono_sim(struct ril_modem *modem);

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2019 Jolla Ltd.
* Copyright (C) 2015-2020 Jolla Ltd.
*
* 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
@@ -13,6 +13,8 @@
* GNU General Public License for more details.
*/
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#include "ril_radio.h"
#include "ril_util.h"
#include "ril_log.h"

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2018 Jolla Ltd.
* Copyright (C) 2015-2020 Jolla Ltd.
*
* 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
@@ -13,6 +13,8 @@
* GNU General Public License for more details.
*/
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#include "ril_sim_card.h"
#include "ril_radio.h"
#include "ril_util.h"

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2016-2019 Jolla Ltd.
* Copyright (C) 2016-2020 Jolla Ltd.
*
* 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
@@ -13,6 +13,8 @@
* GNU General Public License for more details.
*/
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#include "ril_sim_settings.h"
#include "ril_log.h"

View File

@@ -237,6 +237,17 @@ socket=/dev/socket/rild
#
#networkModeTimeout=20000
# Comma-separated signal strength range, in dBm.
#
# These values are used for translating dBm values returned by the modem in
# LTE mode into signal strength percentage. If you are getting significantly
# different signal strength readings in GSM and LTE modes, you may need to
# tweak those.
#
# Default -100,-60
#
#signalStrengthRange=-100,-60
# Cycle radio power at startup.
#
# Default true (cycle the power)
@@ -297,3 +308,11 @@ socket=/dev/socket/rild
# Default auto
#
#deviceStateTracking=auto
# On some phones (such as Jolla C), even if the slot which has been
# using LTE gets powered off, we still need to explicitely set its
# preferred mode to GSM, to make LTE machinery available to the other slot.
#
# Default true (false for MTK RILs)
#
#forceGsmWhenRadioOff=true

View File

@@ -1,7 +1,8 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2019 Jolla Ltd.
* Copyright (C) 2015-2020 Jolla Ltd.
* Copyright (C) 2019-2020 Open Mobile Platform LLC.
*
* 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
@@ -54,6 +55,8 @@ struct ril_slot_config {
enum ril_pref_net_type lte_network_mode;
enum ril_pref_net_type umts_network_mode;
int network_mode_timeout;
int signal_strength_dbm_weak;
int signal_strength_dbm_strong;
gboolean query_available_band_mode;
gboolean empty_pin_query;
gboolean radio_power_cycle;
@@ -62,6 +65,7 @@ struct ril_slot_config {
gboolean enable_cbs;
gboolean enable_stk;
gboolean network_selection_manual_0;
gboolean force_gsm_when_radio_off;
gboolean use_data_profiles;
guint mms_data_profile_id;
GUtilInts *local_hangup_reasons;

View File

@@ -2,6 +2,7 @@
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2019 Jolla Ltd.
* Copyright (C) 2019 Open Mobile Platform LLC.
*
* 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
@@ -116,6 +117,14 @@ gboolean ril_vendor_data_call_parse(RilVendor *self,
data_call_parse(self, call, ver, rilp);
}
gboolean ril_vendor_signal_strength_parse(RilVendor *self,
struct ril_vendor_signal_strength *signal_strength,
GRilIoParser *rilp)
{
return G_LIKELY(self) && RIL_VENDOR_GET_CLASS(self)->
signal_strength_parse(self, signal_strength, rilp);
}
static void ril_vendor_default_set_network(RilVendor *self,
struct ril_network *network)
{
@@ -160,6 +169,13 @@ static gboolean ril_vendor_default_data_call_parse(RilVendor *self,
return FALSE;
}
static gboolean ril_vendor_default_signal_strength_parse(RilVendor *self,
struct ril_vendor_signal_strength *signal_strength,
GRilIoParser *rilp)
{
return FALSE;
}
void ril_vendor_init_base(RilVendor *self, GRilIoChannel *io)
{
self->io = grilio_channel_ref(io);
@@ -190,6 +206,7 @@ static void ril_vendor_class_init(RilVendorClass* klass)
klass->set_attach_apn_req = ril_vendor_default_set_attach_apn_req;
klass->data_call_req = ril_vendor_default_data_call_req;
klass->data_call_parse = ril_vendor_default_data_call_parse;
klass->signal_strength_parse = ril_vendor_default_signal_strength_parse;
}
/*

View File

@@ -2,6 +2,7 @@
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2016-2019 Jolla Ltd.
* Copyright (C) 2019 Open Mobile Platform LLC.
*
* 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
@@ -25,6 +26,7 @@ struct ril_vendor_defaults {
gboolean enable_stk;
gboolean query_available_band_mode;
gboolean use_data_profiles;
gboolean force_gsm_when_radio_off;
guint mms_data_profile_id;
};
@@ -37,6 +39,12 @@ struct ril_vendor_driver {
const struct ril_slot_config *cfg);
};
struct ril_vendor_signal_strength {
gint32 gsm; /* (0-31, 99) per TS 27.007 8.5 */
gint32 lte; /* (0-31, 99) per TS 27.007 8.5 */
gint32 qdbm; /* 4*dBm, 0 if none */
};
const struct ril_vendor_driver *ril_vendor_find_driver(const char *name);
struct ril_vendor *ril_vendor_create
(const struct ril_vendor_driver *vendor, GRilIoChannel *io,
@@ -63,6 +71,9 @@ GRilIoRequest *ril_vendor_data_call_req(struct ril_vendor *vendor, int tech,
gboolean ril_vendor_data_call_parse(struct ril_vendor *vendor,
struct ril_data_call *call, int version,
GRilIoParser *rilp);
gboolean ril_vendor_signal_strength_parse(struct ril_vendor *vendor,
struct ril_vendor_signal_strength *signal_strength,
GRilIoParser *rilp);
/* Put vendor driver descriptors to the "__vendor" section */
#define RIL_VENDOR_DRIVER_DEFINE(name) const struct ril_vendor_driver name \

View File

@@ -2,6 +2,7 @@
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2019 Jolla Ltd.
* Copyright (C) 2019 Open Mobile Platform LLC.
*
* 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
@@ -42,6 +43,9 @@ typedef struct ril_vendor_class {
gboolean (*data_call_parse)(RilVendor *vendor,
struct ril_data_call *call, int version,
GRilIoParser *rilp);
gboolean (*signal_strength_parse)(RilVendor *vendor,
struct ril_vendor_signal_strength *signal_strength,
GRilIoParser *rilp);
} RilVendorClass;
GType ril_vendor_get_type(void);

View File

@@ -2,6 +2,7 @@
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2016-2019 Jolla Ltd.
* Copyright (C) 2019 Open Mobile Platform LLC.
*
* 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
@@ -79,6 +80,8 @@ struct ril_mtk_flavor {
enum ril_auth auth, const char *proto);
gboolean (*data_call_parse_fn)(struct ril_data_call *call,
int version, GRilIoParser *rilp);
gboolean (*signal_strength_fn)(struct ril_vendor_signal_strength *sig,
GRilIoParser *rilp);
};
/* MTK specific RIL messages (actual codes differ from model to model!) */
@@ -401,6 +404,96 @@ static gboolean ril_vendor_mtk_data_call_parse(RilVendor *vendor,
data_call_parse(vendor, call, version, rilp);
}
static gboolean ril_vendor_mtk_signal_strength_1
(struct ril_vendor_signal_strength *signal, GRilIoParser *rilp)
{
if (grilio_parser_bytes_remaining(rilp) == 64) {
gint32 rsrp = 0, rssi = 0;
/* GW_SignalStrength */
grilio_parser_get_int32(rilp, &signal->gsm);
grilio_parser_get_int32(rilp, NULL); /* bitErrorRate */
/* CDMA_SignalStrength */
grilio_parser_get_int32(rilp, NULL); /* dbm */
grilio_parser_get_int32(rilp, NULL); /* ecio */
/* EVDO_SignalStrength */
grilio_parser_get_int32(rilp, NULL); /* dbm */
grilio_parser_get_int32(rilp, NULL); /* ecio */
grilio_parser_get_int32(rilp, NULL); /* signalNoiseRatio */
/* LTE_SignalStrength */
grilio_parser_get_int32(rilp, &signal->lte);
grilio_parser_get_int32(rilp, &rsrp); /* rsrp */
grilio_parser_get_int32(rilp, NULL); /* rsrq */
grilio_parser_get_int32(rilp, NULL); /* rssnr */
grilio_parser_get_int32(rilp, NULL); /* cqi */
/* ???? */
grilio_parser_get_int32(rilp, NULL);
grilio_parser_get_int32(rilp, &rssi);
grilio_parser_get_int32(rilp, NULL);
grilio_parser_get_int32(rilp, NULL);
signal->qdbm = (rssi > 0 && rssi != INT_MAX) ? (-4 * rssi) :
(rsrp >= 44 && rsrp <= 140) ? (-4 * rsrp) : 0;
return TRUE;
}
return FALSE;
}
static gboolean ril_vendor_mtk_signal_strength_2
(struct ril_vendor_signal_strength *signal, GRilIoParser *rilp)
{
if (grilio_parser_bytes_remaining(rilp) == 64) {
gint32 rsrp = 0, is_gsm = 0, rssi_qdbm = 0;
/* GW_SignalStrength */
grilio_parser_get_int32(rilp, &signal->gsm);
grilio_parser_get_int32(rilp, NULL); /* bitErrorRate */
/* CDMA_SignalStrength */
grilio_parser_get_int32(rilp, NULL); /* dbm */
grilio_parser_get_int32(rilp, NULL); /* ecio */
/* EVDO_SignalStrength */
grilio_parser_get_int32(rilp, NULL); /* dbm */
grilio_parser_get_int32(rilp, NULL); /* ecio */
grilio_parser_get_int32(rilp, NULL); /* signalNoiseRatio */
/* LTE_SignalStrength */
grilio_parser_get_int32(rilp, &signal->lte);
grilio_parser_get_int32(rilp, &rsrp); /* rsrp */
grilio_parser_get_int32(rilp, NULL); /* rsrq */
grilio_parser_get_int32(rilp, NULL); /* rssnr */
grilio_parser_get_int32(rilp, NULL); /* cqi */
/* WCDMA_SignalStrength */
grilio_parser_get_int32(rilp, &is_gsm); /* isGsm */
grilio_parser_get_int32(rilp, &rssi_qdbm); /* rssiQdbm */
grilio_parser_get_int32(rilp, NULL); /* rscpQdbm */
grilio_parser_get_int32(rilp, NULL); /* Ecn0Qdbm*/
signal->qdbm = (is_gsm == 1 && rssi_qdbm < 0) ? rssi_qdbm :
(rsrp >= 44 && rsrp <= 140) ? (-4 * rsrp) : 0;
return TRUE;
}
return FALSE;
}
static gboolean ril_vendor_mtk_signal_strength_parse(RilVendor *vendor,
struct ril_vendor_signal_strength *signal,
GRilIoParser *rilp)
{
const struct ril_mtk_flavor *flavor = RIL_VENDOR_MTK(vendor)->flavor;
return flavor->signal_strength_fn ?
flavor->signal_strength_fn(signal, rilp) :
RIL_VENDOR_CLASS(ril_vendor_mtk_parent_class)->
signal_strength_parse(vendor, signal, rilp);
}
static void ril_vendor_mtk_get_defaults(struct ril_vendor_defaults *defaults)
{
/*
@@ -415,6 +508,7 @@ static void ril_vendor_mtk_get_defaults(struct ril_vendor_defaults *defaults)
defaults->query_available_band_mode = FALSE;
defaults->empty_pin_query = FALSE;
defaults->legacy_imei_query = TRUE;
defaults->force_gsm_when_radio_off = FALSE;
}
static void ril_vendor_mtk_base_init(RilVendorMtk *self, GRilIoChannel *io,
@@ -496,20 +590,23 @@ static void ril_vendor_mtk_class_init(RilVendorMtkClass* klass)
klass->set_attach_apn_req = ril_vendor_mtk_set_attach_apn_req;
klass->data_call_req = ril_vendor_mtk_data_call_req;
klass->data_call_parse = ril_vendor_mtk_data_call_parse;
klass->signal_strength_parse = ril_vendor_mtk_signal_strength_parse;
}
static const struct ril_mtk_flavor ril_mtk_flavor1 = {
.name = "mtk1",
.msg = &msg_mtk1,
.build_attach_apn_req_fn = &ril_vendor_mtk_build_attach_apn_req_1,
.data_call_parse_fn = NULL
.data_call_parse_fn = NULL,
.signal_strength_fn = &ril_vendor_mtk_signal_strength_1
};
static const struct ril_mtk_flavor ril_mtk_flavor2 = {
.name = "mtk2",
.msg = &msg_mtk2,
.build_attach_apn_req_fn = &ril_vendor_mtk_build_attach_apn_req_2,
.data_call_parse_fn = &ril_vendor_mtk_data_call_parse_v6
.data_call_parse_fn = &ril_vendor_mtk_data_call_parse_v6,
.signal_strength_fn = &ril_vendor_mtk_signal_strength_2
};
#define DEFAULT_MTK_TYPE (&ril_mtk_flavor1)

0
ofono/drivers/xmm7modem/ims.c Executable file → Normal file
View File

View File

@@ -1,7 +1,8 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2019 Jolla Ltd.
* Copyright (C) 2019-2020 Jolla Ltd.
* Copyright (C) 2020 Open Mobile Platform LLC.
*
* 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
@@ -25,7 +26,7 @@ extern "C" {
enum ofono_dbus_access {
OFONO_DBUS_ACCESS_DENY, /* Deny access */
OFONO_DBUS_ACCESS_ALLOW, /* Allow access */
OFONO_DBUS_ACCESS_DONT_CARE, /* No decision */
OFONO_DBUS_ACCESS_DONT_CARE /* No decision */
};
enum ofono_dbus_access_intf {
@@ -38,6 +39,7 @@ enum ofono_dbus_access_intf {
OFONO_DBUS_ACCESS_INTF_SIMMGR, /* org.ofono.SimManager */
OFONO_DBUS_ACCESS_INTF_MODEM, /* org.ofono.Modem */
OFONO_DBUS_ACCESS_INTF_RADIOSETTINGS, /* org.ofono.RadioSettings */
OFONO_DBUS_ACCESS_INTF_STK, /* org.ofono.SimToolkit */
OFONO_DBUS_ACCESS_INTF_COUNT
};
@@ -116,6 +118,12 @@ enum ofono_dbus_access_radiosettings_method {
OFONO_DBUS_ACCESS_RADIOSETTINGS_METHOD_COUNT
};
/* OFONO_DBUS_ACCESS_INTF_STK */
enum ofono_dbus_access_stk_method {
OFONO_DBUS_ACCESS_STK_REGISTER_AGENT,
OFONO_DBUS_ACCESS_STK_METHOD_COUNT
};
#define OFONO_DBUS_ACCESS_PRIORITY_LOW (-100)
#define OFONO_DBUS_ACCESS_PRIORITY_DEFAULT (0)
#define OFONO_DBUS_ACCESS_PRIORITY_HIGH (100)

View File

@@ -139,7 +139,12 @@ struct ofono_voicecall_driver {
/* Dials the last number again, this handles the hfp profile last number
* dialing with the +BLDN AT command
*/
void (*dial_last)(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data);
void (*dial_last)(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb,
void *data);
/* dials a number at a given memory location */
void (*dial_memory)(struct ofono_voicecall *vc,
unsigned int memory_location, ofono_voicecall_cb_t cb,
void *data);
};
void ofono_voicecall_en_list_notify(struct ofono_voicecall *vc,

View File

@@ -43,6 +43,7 @@
#include <ofono/ussd.h>
#include <ofono/gprs.h>
#include <ofono/gprs-context.h>
#include <ofono/lte.h>
#include <ofono/radio-settings.h>
#include <ofono/location-reporting.h>
#include <ofono/log.h>
@@ -483,6 +484,8 @@ static void gobi_post_sim(struct ofono_modem *modem)
DBG("%p", modem);
ofono_lte_create(modem, "qmimodem", data->device);
if (data->features & GOBI_CAT)
ofono_stk_create(modem, 0, "qmimodem", data->device);
else if (data->features & GOBI_CAT_OLD)

View File

@@ -1,7 +1,8 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2017-2019 Jolla Ltd.
* Copyright (C) 2017-2020 Jolla Ltd.
* Copyright (C) 2019-2020 Open Mobile Platform LLC.
*
* 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
@@ -22,6 +23,7 @@
#include <gutil_macros.h>
#include <string.h>
#include <ofono/storage.h>
#include <ofono/watch.h>
#include "src/ofono.h"
@@ -44,6 +46,12 @@ enum ofono_watch_events {
WATCH_EVENT_COUNT
};
enum sim_auto_select {
SIM_AUTO_SELECT_OFF,
SIM_AUTO_SELECT_ON,
SIM_AUTO_SELECT_ONCE
};
struct sailfish_manager_priv {
struct sailfish_manager pub; /* Public part */
struct sailfish_slot_driver_reg *drivers;
@@ -52,6 +60,8 @@ struct sailfish_manager_priv {
struct sailfish_slot_priv *data_slot;
struct sailfish_slot_priv *mms_slot;
sailfish_slot_ptr *slots;
enum sim_auto_select auto_data_sim;
gboolean auto_data_sim_done;
int slot_count;
guint init_countdown;
guint init_id;
@@ -99,6 +109,11 @@ struct sailfish_slot_priv {
int index;
};
/* Read-only config */
#define SF_CONFIG_FILE "main.conf"
#define SF_CONFIG_GROUP "ModemManager"
#define SF_CONFIG_KEY_AUTO_DATA_SIM "AutoSelectDataSim"
/* "ril" is used for historical reasons */
#define SF_STORE "ril"
#define SF_STORE_GROUP "Settings"
@@ -106,6 +121,7 @@ struct sailfish_slot_priv {
#define SF_STORE_DEFAULT_VOICE_SIM "DefaultVoiceSim"
#define SF_STORE_DEFAULT_DATA_SIM "DefaultDataSim"
#define SF_STORE_SLOTS_SEP ","
#define SF_STORE_AUTO_DATA_SIM_DONE "AutoSelectDataSimDone"
/* The file where error statistics is stored. Again "rilerror" is historical */
#define SF_ERROR_STORAGE "rilerror" /* File name */
@@ -145,6 +161,50 @@ static inline void sailfish_slot_set_data_role(struct sailfish_slot_priv *s,
}
}
static gboolean sailfish_config_get_enum(GKeyFile *file, const char *group,
const char *key, int *result,
const char *name, int value, ...)
{
char *str = g_key_file_get_string(file, group, key, NULL);
if (str) {
/*
* Some people are thinking that # is a comment
* anywhere on the line, not just at the beginning
*/
char *comment = strchr(str, '#');
if (comment) *comment = 0;
g_strstrip(str);
if (strcasecmp(str, name)) {
va_list args;
va_start(args, value);
while ((name = va_arg(args, char*)) != NULL) {
value = va_arg(args, int);
if (!strcasecmp(str, name)) {
break;
}
}
va_end(args);
}
if (!name) {
ofono_error("Invalid %s config value (%s)", key, str);
}
g_free(str);
if (name) {
if (result) {
*result = value;
}
return TRUE;
}
}
return FALSE;
}
/* Update modem paths and emit D-Bus signal if necessary */
static void sailfish_manager_update_modem_paths_full
(struct sailfish_manager_priv *p)
@@ -588,6 +648,27 @@ static struct sailfish_slot_priv *sailfish_manager_find_slot_imsi
}
}
static gboolean sailfish_manager_all_sims_are_initialized_proc
(struct sailfish_slot_priv *s, void *user_data)
{
if (s->pub.sim_present && s->pub.enabled && !s->watch->imsi) {
*((gboolean*)user_data) = FALSE;
return SF_LOOP_DONE;
} else {
return SF_LOOP_CONTINUE;
}
}
static gboolean sailfish_manager_all_sims_are_initialized
(struct sailfish_manager_priv *p)
{
gboolean result = TRUE;
sailfish_manager_foreach_slot(p,
sailfish_manager_all_sims_are_initialized_proc, &result);
return result;
}
/* Returns the event mask to be passed to sailfish_manager_dbus_signal.
* The caller has a chance to OR it with other bits */
static int sailfish_manager_update_modem_paths(struct sailfish_manager_priv *p)
@@ -615,7 +696,7 @@ static int sailfish_manager_update_modem_paths(struct sailfish_manager_priv *p)
* previously selected voice SIM is inserted, we will switch
* back to it.
*
* There is no such fallback for the data.
* A similar behavior can be configured for data SIM too.
*/
if (!slot) {
slot = sailfish_manager_find_slot_imsi(p, NULL);
@@ -648,13 +729,52 @@ static int sailfish_manager_update_modem_paths(struct sailfish_manager_priv *p)
slot = sailfish_manager_find_slot_imsi(p, NULL);
}
} else {
/*
* Should we automatically select the default data sim
* on a multisim phone that has only one sim inserted?
*/
slot = NULL;
}
/* Check if we need to auto-select data SIM (always or once) */
if (!slot && (p->auto_data_sim == SIM_AUTO_SELECT_ON ||
(p->auto_data_sim == SIM_AUTO_SELECT_ONCE &&
!p->auto_data_sim_done))) {
/*
* To actually make a selection we need all present SIMs
* to be initialized. Otherwise we may end up endlessly
* switching data SIMs back and forth.
*/
if (sailfish_manager_all_sims_are_initialized(p)) {
slot = sailfish_manager_find_slot_imsi(p, NULL);
if (slot && slot->watch->imsi && slot->watch->online &&
p->auto_data_sim == SIM_AUTO_SELECT_ONCE) {
const char *imsi = slot->watch->imsi;
/*
* Data SIM only needs to be auto-selected
* once and it's done. Write that down.
*/
DBG("Default data sim set to %s once", imsi);
p->auto_data_sim_done = TRUE;
g_key_file_set_boolean(p->storage,
SF_STORE_GROUP,
SF_STORE_AUTO_DATA_SIM_DONE,
p->auto_data_sim_done);
g_free(p->default_data_imsi);
p->pub.default_data_imsi =
p->default_data_imsi = g_strdup(imsi);
g_key_file_set_string(p->storage,
SF_STORE_GROUP,
SF_STORE_DEFAULT_DATA_SIM,
imsi);
storage_sync(NULL, SF_STORE, p->storage);
sailfish_manager_dbus_signal(p->dbus,
SAILFISH_MANAGER_SIGNAL_DATA_IMSI);
}
} else {
DBG("Skipping auto-selection of data SIM");
}
}
if (slot && !slot->watch->online) {
slot = NULL;
}
@@ -1274,6 +1394,29 @@ static struct sailfish_manager_priv *sailfish_manager_priv_new()
struct sailfish_manager_priv *p =
g_slice_new0(struct sailfish_manager_priv);
GKeyFile *conf = g_key_file_new();
char* fn = g_build_filename(ofono_config_dir(), SF_CONFIG_FILE, NULL);
/* Load config */
if (g_key_file_load_from_file(conf, fn, 0, NULL)) {
int ival;
DBG("Loading configuration file %s", fn);
if (sailfish_config_get_enum(conf, SF_CONFIG_GROUP,
SF_CONFIG_KEY_AUTO_DATA_SIM, &ival,
"off", SIM_AUTO_SELECT_OFF,
"once", SIM_AUTO_SELECT_ONCE,
"always", SIM_AUTO_SELECT_ON,
"on", SIM_AUTO_SELECT_ON, NULL)) {
DBG("Automatic data SIM selection: %s",
ival == SIM_AUTO_SELECT_ONCE ? "once":
ival == SIM_AUTO_SELECT_ON ? "on":
"off");
p->auto_data_sim = ival;
}
}
g_key_file_free(conf);
g_free(fn);
/* Load settings */
p->storage = storage_open(NULL, SF_STORE);
@@ -1283,6 +1426,8 @@ static struct sailfish_manager_priv *sailfish_manager_priv_new()
p->pub.default_data_imsi = p->default_data_imsi =
g_key_file_get_string(p->storage, SF_STORE_GROUP,
SF_STORE_DEFAULT_DATA_SIM, NULL);
p->auto_data_sim_done = g_key_file_get_boolean(p->storage,
SF_STORE_GROUP, SF_STORE_AUTO_DATA_SIM_DONE, NULL);
DBG("Default voice sim is %s", p->default_voice_imsi ?
p->default_voice_imsi : "(auto)");

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2017-2019 Jolla Ltd.
* Copyright (C) 2017-2020 Jolla Ltd.
*
* 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
@@ -13,6 +13,8 @@
* GNU General Public License for more details.
*/
#define GLIB_DISABLE_DEPRECATION_WARNINGS
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

View File

@@ -1731,6 +1731,8 @@ static gboolean create_modem(gpointer key, gpointer value, gpointer user_data)
continue;
if (driver_list[i].setup(modem) == TRUE) {
ofono_modem_set_string(modem->modem, "SystemPath",
syspath);
ofono_modem_register(modem->modem);
return FALSE;
}

365
ofono/src/config.c Normal file
View File

@@ -0,0 +1,365 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2015-2019 Jolla Ltd.
* Copyright (C) 2019 Open Mobile Platform LLC.
*
* 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.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include "ofono.h"
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
/**
* The config_merge_files() function does the following:
*
* 1. Loads the specified key file (say, "/etc/foo.conf")
* 2. Scans the subdirectory named after the file (e.g. "/etc/foo.d/")
* for the files with the same suffix as the main file (e.g. "*.conf")
* 3. Sorts the files from the subdirectory (alphabetically)
* 4. Merges the contents of the additional files with the main file
* according to their sort order.
*
* When the entries are merged, keys and groups overwrite the exising
* ones by default. Keys can be suffixed with special characters to
* remove or modify the existing entries instead:
*
* ':' Sets the (default) value if the key is missing
* '+' Appends values to the string list
* '?' Appends only new (non-existent) values to the string list
* '-' Removes the values from the string list
*
* Both keys and groups can be prefixed with '!' to remove the entire key
* or group.
*
* For example if we merge these two files:
*
* /etc/foo.conf:
*
* [foo]
* a=1
* b=2,3
* c=4
* d=5
* [bar]
* e=5
*
* /etc/foo.d/bar.conf:
*
* [foo]
* a+=2
* b-=2
* c=5
* !d
* [!bar]
*
* we end up with this:
*
* [foo]
* a=1
* b=2,3
* c=5
*
* Note that the list separator is assumed to be ',' (rather than default ';').
* The keyfile passed to config_merge_files() should use the same list
* separator, because the default values are copied from the config files
* as is.
*/
static gint config_sort_files(gconstpointer a, gconstpointer b)
{
/* The comparison function for g_ptr_array_sort() doesn't take
* the pointers from the array as arguments, it takes pointers
* to the pointers in the array. */
return strcmp(*(char**)a, *(char**)b);
}
static char **config_collect_files(const char *path, const char *suffix)
{
/* Returns sorted list of regular files in the directory,
* optionally having the specified suffix (e.g. ".conf").
* Returns NULL if nothing appropriate has been found. */
char **files = NULL;
DIR *d = opendir(path);
if (d) {
GPtrArray *list = g_ptr_array_new();
const struct dirent *p;
while ((p = readdir(d)) != NULL) {
/* No need to even stat . and .. */
if (strcmp(p->d_name, ".") &&
strcmp(p->d_name, "..") && (!suffix ||
g_str_has_suffix(p->d_name, suffix))) {
struct stat st;
char *buf = g_strconcat(path, "/", p->d_name,
NULL);
if (!stat(buf, &st) && S_ISREG(st.st_mode)) {
g_ptr_array_add(list, buf);
} else {
g_free(buf);
}
}
}
if (list->len > 0) {
g_ptr_array_sort(list, config_sort_files);
g_ptr_array_add(list, NULL);
files = (char**)g_ptr_array_free(list, FALSE);
} else {
g_ptr_array_free(list, TRUE);
}
closedir(d);
}
return files;
}
static int config_list_find(char **list, gsize len, const char *value)
{
guint i;
for (i = 0; i < len; i++) {
if (!strcmp(list[i], value)) {
return i;
}
}
return -1;
}
static void config_list_append(GKeyFile *conf, GKeyFile *k,
const char *group, const char *key,
char **values, gsize n, gboolean unique)
{
/* Note: will steal strings from values */
if (n > 0) {
int i;
gsize len = 0;
gchar **list = g_key_file_get_string_list(conf, group, key,
&len, NULL);
GPtrArray *newlist = g_ptr_array_new_full(0, g_free);
for (i = 0; i < (int)len; i++) {
g_ptr_array_add(newlist, list[i]);
}
for (i = 0; i < (int)n; i++) {
char *val = values[i];
if (!unique || config_list_find((char**)
newlist->pdata, newlist->len, val) < 0) {
/* Move the string to the new list */
g_ptr_array_add(newlist, val);
memmove(values + i, values + i + 1,
sizeof(char*) * (n - i));
i--;
n--;
}
}
if (newlist->len > len) {
g_key_file_set_string_list(conf, group, key,
(const gchar * const *) newlist->pdata,
newlist->len);
}
/* Strings are deallocated by GPtrArray */
g_ptr_array_free(newlist, TRUE);
g_free(list);
}
}
static void config_list_remove(GKeyFile *conf, GKeyFile *k,
const char *group, const char *key, char **values, gsize n)
{
if (n > 0) {
gsize len = 0;
gchar **list = g_key_file_get_string_list(conf, group, key,
&len, NULL);
if (len > 0) {
gsize i;
const gsize oldlen = len;
for (i = 0; i < n; i++) {
int pos;
/* Remove all matching values */
while ((pos = config_list_find(list, len,
values[i])) >= 0) {
g_free(list[pos]);
memmove(list + pos, list + pos + 1,
sizeof(char*) * (len - pos));
len--;
}
}
if (len < oldlen) {
g_key_file_set_string_list(conf, group, key,
(const gchar * const *) list, len);
}
}
g_strfreev(list);
}
}
static void config_merge_group(GKeyFile *conf, GKeyFile *k,
const char *group)
{
gsize i, n = 0;
char **keys = g_key_file_get_keys(k, group, &n, NULL);
for (i=0; i<n; i++) {
char *key = keys[i];
if (key[0] == '!') {
if (key[1]) {
g_key_file_remove_key(conf, group, key+1, NULL);
}
} else {
const gsize len = strlen(key);
const char last = (len > 0) ? key[len-1] : 0;
if (last == '+' || last == '?') {
gsize count = 0;
gchar **values = g_key_file_get_string_list(k,
group, key, &count, NULL);
key[len-1] = 0;
config_list_append(conf, k, group, key,
values, count, last == '?');
g_strfreev(values);
} else if (last == '-') {
gsize count = 0;
gchar **values = g_key_file_get_string_list(k,
group, key, &count, NULL);
key[len-1] = 0;
config_list_remove(conf, k, group, key,
values, count);
g_strfreev(values);
} else {
/* Overwrite the value (it must exist in k) */
gchar *value = g_key_file_get_value(k, group,
key, NULL);
if (last == ':') {
/* Default value */
key[len-1] = 0;
if (!g_key_file_has_key(conf,
group, key, NULL)) {
g_key_file_set_value(conf,
group, key, value);
}
} else {
g_key_file_set_value(conf, group, key,
value);
}
g_free(value);
}
}
}
g_strfreev(keys);
}
static void config_merge_keyfile(GKeyFile *conf, GKeyFile *k)
{
gsize i, n = 0;
char **groups = g_key_file_get_groups(k, &n);
for (i=0; i<n; i++) {
const char *group = groups[i];
if (group[0] == '!') {
g_key_file_remove_group(conf, group + 1, NULL);
} else {
config_merge_group(conf, k, group);
}
}
g_strfreev(groups);
}
static void config_merge_file(GKeyFile *conf, const char *file)
{
GKeyFile *k = g_key_file_new();
g_key_file_set_list_separator(k, ',');
if (g_key_file_load_from_file(k, file, 0, NULL)) {
config_merge_keyfile(conf, k);
}
g_key_file_unref(k);
}
void config_merge_files(GKeyFile *conf, const char *file)
{
if (conf && file && file[0]) {
char *dot = strrchr(file, '.');
const char *suffix;
char *dir;
char **files;
if (!dot) {
dir = g_strconcat(file, ".d", NULL);
suffix = NULL;
} else if (!dot[1]) {
dir = g_strconcat(file, "d", NULL);
suffix = NULL;
} else {
/* 2 bytes for ".d" and 1 for NULL terminator */
dir = g_malloc(dot - file + 3);
strncpy(dir, file, dot - file);
strcpy(dir + (dot - file), ".d");
suffix = dot + 1;
}
files = config_collect_files(dir, suffix);
g_free(dir);
/* Load the main config */
if (g_file_test(file, G_FILE_TEST_EXISTS)) {
DBG("Loading %s", file);
config_merge_file(conf, file);
}
if (files) {
char **ptr;
for (ptr = files; *ptr; ptr++) {
DBG("Merging %s", *ptr);
config_merge_file(conf, *ptr);
}
g_strfreev(files);
}
}
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -1,7 +1,8 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2019 Jolla Ltd.
* Copyright (C) 2019-2020 Jolla Ltd.
* Copyright (C) 2020 Open Mobile Platform LLC.
*
* 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
@@ -41,6 +42,8 @@ const char *ofono_dbus_access_intf_name(enum ofono_dbus_access_intf intf)
return OFONO_MODEM_INTERFACE;
case OFONO_DBUS_ACCESS_INTF_RADIOSETTINGS:
return OFONO_RADIO_SETTINGS_INTERFACE;
case OFONO_DBUS_ACCESS_INTF_STK:
return OFONO_STK_INTERFACE;
case OFONO_DBUS_ACCESS_INTF_COUNT:
break;
}
@@ -165,6 +168,14 @@ const char *ofono_dbus_access_method_name(enum ofono_dbus_access_intf intf,
break;
}
break;
case OFONO_DBUS_ACCESS_INTF_STK:
switch ((enum ofono_dbus_access_stk_method)method) {
case OFONO_DBUS_ACCESS_STK_REGISTER_AGENT:
return "RegisterAgent";
case OFONO_DBUS_ACCESS_STK_METHOD_COUNT:
break;
}
break;
case OFONO_DBUS_ACCESS_INTF_COUNT:
break;
}

View File

@@ -805,6 +805,7 @@ void __ofono_modem_append_properties(struct ofono_modem *modem,
struct ofono_devinfo *info;
dbus_bool_t emergency = ofono_modem_get_emergency_mode(modem);
const char *strtype;
const char *system_path;
ofono_dbus_dict_append(dict, "Online", DBUS_TYPE_BOOLEAN,
&modem->online);
@@ -845,6 +846,11 @@ void __ofono_modem_append_properties(struct ofono_modem *modem,
&info->svn);
}
system_path = ofono_modem_get_string(modem, "SystemPath");
if (system_path)
ofono_dbus_dict_append(dict, "SystemPath", DBUS_TYPE_STRING,
&system_path);
interfaces = g_new0(char *, g_slist_length(modem->interface_list) + 1);
for (i = 0, l = modem->interface_list; l; l = l->next, i++)
interfaces[i] = l->data;

View File

@@ -4,6 +4,7 @@
*
* Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
* Copyright (C) 2015-2019 Jolla Ltd.
* Copyright (C) 2019 Open Mobile Platform LLC.
*
* 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
@@ -684,3 +685,6 @@ int mnclength(int mcc, int mnc);
#include <ofono/netmon.h>
#include <ofono/lte.h>
#include <ofono/ims.h>
void __ofono_set_config_dir(const char *dir);
void config_merge_files(GKeyFile *conf, const char *file);

View File

@@ -139,8 +139,17 @@ static void sim_auth_unregister(struct ofono_atom *atom)
struct ofono_sim_auth *sa = __ofono_atom_get_data(atom);
free_apps(sa);
g_free(sa->nai);
g_free(sa->pending);
if (sa->pending) {
__ofono_dbus_pending_reply(&sa->pending->msg,
__ofono_error_sim_not_ready(sa->pending->msg));
__ofono_sim_remove_session_watch(sa->pending->session,
sa->pending->watch_id);
g_free(sa->pending);
sa->pending = NULL;
}
}
static void sim_auth_remove(struct ofono_atom *atom)

View File

@@ -3,6 +3,8 @@
* oFono - Open Source Telephony
*
* Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
* Copyright (C) 2020 Jolla Ltd.
* Copyright (C) 2020 Open Mobile Platform LLC.
*
* 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
@@ -725,6 +727,12 @@ static DBusMessage *stk_register_agent(DBusConnection *conn,
if (!dbus_validate_path(agent_path, NULL))
return __ofono_error_invalid_format(msg);
if (!__ofono_dbus_access_method_allowed(dbus_message_get_sender(msg),
OFONO_DBUS_ACCESS_INTF_STK,
OFONO_DBUS_ACCESS_STK_REGISTER_AGENT,
agent_path))
return __ofono_error_access_denied(msg);
stk->default_agent = stk_agent_new(agent_path,
dbus_message_get_sender(msg),
FALSE);

View File

@@ -3,6 +3,7 @@
* oFono - Open Source Telephony
*
* Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
* Copyright (C) 2015-2019 Jolla Ltd.
*
* 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
@@ -36,10 +37,19 @@
#include <glib.h>
#include "storage.h"
#include "ofono.h"
static char* config_dir = NULL;
void __ofono_set_config_dir(const char *dir)
{
g_free(config_dir);
config_dir = g_strdup(dir);
}
const char *ofono_config_dir(void)
{
return CONFIGDIR;
return config_dir ? config_dir : CONFIGDIR;
}
const char *ofono_storage_dir(void)

View File

@@ -1798,7 +1798,7 @@ static DBusMessage *manager_dial(DBusConnection *conn,
return __ofono_error_failed(msg);
}
static void manager_dial_last_callback(const struct ofono_error *error,
static void manager_dial_hfp_callback(const struct ofono_error *error,
void *data)
{
struct ofono_voicecall *vc = data;
@@ -1827,8 +1827,8 @@ error:
__ofono_error_failed(vc->pending));
}
static int voicecall_dial_last(struct ofono_voicecall *vc,
ofono_voicecall_cb_t cb, void *data)
static int voicecall_dial_hfp(struct ofono_voicecall *vc, unsigned int position,
ofono_voicecall_cb_t cb, void *data)
{
struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom);
@@ -1838,9 +1838,6 @@ static int voicecall_dial_last(struct ofono_voicecall *vc,
if (ofono_modem_get_online(modem) == FALSE)
return -ENETDOWN;
if (vc->driver->dial_last == NULL)
return -ENOTSUP;
if (voicecalls_have_incoming(vc))
return -EBUSY;
@@ -1851,7 +1848,18 @@ static int voicecall_dial_last(struct ofono_voicecall *vc,
if (voicecalls_have_active(vc) && voicecalls_have_held(vc))
return -EBUSY;
vc->driver->dial_last(vc, cb, vc);
/* when position is not given we dial the last called number */
if (position == 0) {
if (vc->driver->dial_last == NULL)
return -ENOTSUP;
vc->driver->dial_last(vc, cb, vc);
} else {
if (vc->driver->dial_memory == NULL )
return -ENOTSUP;
vc->driver->dial_memory(vc, position, cb, vc);
}
return 0;
}
@@ -1867,7 +1875,7 @@ static DBusMessage *manager_dial_last(DBusConnection *conn,
vc->pending = dbus_message_ref(msg);
err = voicecall_dial_last(vc, manager_dial_last_callback, vc);
err = voicecall_dial_hfp(vc, 0, manager_dial_hfp_callback, vc);
if (err >= 0)
return NULL;
@@ -1889,6 +1897,44 @@ static DBusMessage *manager_dial_last(DBusConnection *conn,
return __ofono_error_failed(msg);
}
static DBusMessage *manager_dial_memory(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ofono_voicecall *vc = data;
int memory_location;
int err;
if (vc->pending || vc->dial_req || vc->pending_em)
return __ofono_error_busy(msg);
if (dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &memory_location,
DBUS_TYPE_INVALID) == FALSE)
return __ofono_error_invalid_args(msg);
vc->pending = dbus_message_ref(msg);
err = voicecall_dial_hfp(vc, memory_location,
manager_dial_hfp_callback, vc);
if (err >= 0)
return NULL;
vc->pending = NULL;
dbus_message_unref(msg);
switch (err) {
case -EINVAL:
return __ofono_error_invalid_format(msg);
case -ENETDOWN:
return __ofono_error_not_available(msg);
case -ENOTSUP:
return __ofono_error_not_implemented(msg);
}
return __ofono_error_failed(msg);
}
static DBusMessage *manager_transfer(DBusConnection *conn,
DBusMessage *msg, void *data)
{
@@ -2552,6 +2598,9 @@ static const GDBusMethodTable manager_methods[] = {
GDBUS_ARGS({ "path", "o" }),
manager_dial) },
{ GDBUS_ASYNC_METHOD("DialLast", NULL, NULL, manager_dial_last)},
{ GDBUS_ASYNC_METHOD("DialMemory",
GDBUS_ARGS({"memory_location", "u" }), NULL,
manager_dial_memory) },
{ GDBUS_ASYNC_METHOD("Transfer", NULL, NULL, manager_transfer) },
{ GDBUS_ASYNC_METHOD("SwapCalls", NULL, NULL, manager_swap_calls) },
{ GDBUS_ASYNC_METHOD("ReleaseAndAnswer", NULL, NULL,

View File

@@ -20,9 +20,11 @@ TESTS="\
test-dbus-access \
test-gprs-filter \
test-provision \
test-config \
test-watch \
test-ril_util \
test-ril_config \
test-ril_ecclist \
test-ril-transport \
test-ril_vendor \
test-sms-filter \

448
ofono/unit/test-config.c Normal file
View File

@@ -0,0 +1,448 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2019 Open Mobile Platform LLC.
*
* 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 <ofono/log.h>
#include "ofono.h"
#include <gutil_strv.h>
#include <gutil_ints.h>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#define TMP_DIR_TEMPLATE "test-config-XXXXXX"
static gboolean test_keyfile_empty(GKeyFile *k)
{
gsize n = 0;
char **groups = g_key_file_get_groups(k, &n);
g_strfreev(groups);
return !n;
}
static void test_merge_ignore(const char *filename, const char *contents,
const char *dirname, const char *filename1, const char *contents1)
{
char *dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
char *file = g_strconcat(dir, "/", filename, NULL);
char *subdir = g_strconcat(dir, "/", dirname, NULL);
char *file1 = g_strconcat(subdir, "/", filename1, NULL);
GKeyFile *k = g_key_file_new();
char *data;
g_assert(!mkdir(subdir, 0700));
g_assert(g_file_set_contents(file, contents, -1, NULL));
g_assert(g_file_set_contents(file1, contents1, -1, NULL));
DBG("reading %s", file);
config_merge_files(k, file);
data = g_key_file_to_data(k, NULL, NULL);
DBG("\n%s", data);
g_assert(!g_strcmp0(data, contents));
g_free(data);
g_key_file_unref(k);
remove(file);
remove(file1);
remove(subdir);
remove(dir);
g_free(file);
g_free(file1);
g_free(dir);
g_free(subdir);
}
static void test_merge1(const char *conf, const char *conf1, const char *out)
{
char *dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
char *file = g_strconcat(dir, "/foo.conf", NULL);
char *subdir = g_strconcat(dir, "/foo.d", NULL);
char *file1 = g_strconcat(subdir, "/bar.conf", NULL);
GKeyFile *k = g_key_file_new();
char *data;
g_assert(!mkdir(subdir, 0700));
g_assert(g_file_set_contents(file, conf, -1, NULL));
g_assert(g_file_set_contents(file1, conf1, -1, NULL));
DBG("reading %s", file);
g_key_file_set_list_separator(k, ',');
config_merge_files(k, file);
data = g_key_file_to_data(k, NULL, NULL);
DBG("\n%s", data);
g_assert(!g_strcmp0(data, out));
g_free(data);
g_key_file_unref(k);
remove(file);
remove(file1);
remove(subdir);
remove(dir);
g_free(file);
g_free(file1);
g_free(dir);
g_free(subdir);
}
/* ==== merge_basic ==== */
static void test_merge_basic(void)
{
GKeyFile *k = g_key_file_new();
char *nonexistent = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
config_merge_files(NULL, NULL);
remove(nonexistent);
config_merge_files(k, nonexistent);
g_assert(test_keyfile_empty(k));
config_merge_files(k, NULL);
g_assert(test_keyfile_empty(k));
config_merge_files(k, "");
g_assert(test_keyfile_empty(k));
g_key_file_unref(k);
g_free(nonexistent);
}
/* ==== merge_simple ==== */
static void test_merge_simple(void)
{
static const char contents [] = "[foo]\na=1\nb=2\n";
char *dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
char *file = g_strconcat(dir, "/foo.conf", NULL);
char *data;
GKeyFile *k = g_key_file_new();
g_assert(g_file_set_contents(file, contents, -1, NULL));
DBG("reading %s", file);
config_merge_files(k, file);
data = g_key_file_to_data(k, NULL, NULL);
DBG("\n%s", data);
g_assert(!g_strcmp0(data, contents));
g_free(data);
g_key_file_unref(k);
remove(file);
remove(dir);
g_free(file);
g_free(dir);
}
/* ==== merge_empty_dir ==== */
static void test_merge_empty_dir(void)
{
static const char contents [] = "[foo]\na=1\nb=2\n";
char *dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
char *subdir = g_strconcat(dir, "/foo.d", NULL);
char *file = g_strconcat(dir, "/foo.conf", NULL);
GKeyFile *k = g_key_file_new();
char *data;
g_assert(!mkdir(subdir, 0700));
g_assert(g_file_set_contents(file, contents, -1, NULL));
DBG("reading %s", file);
config_merge_files(k, file);
data = g_key_file_to_data(k, NULL, NULL);
DBG("\n%s", data);
g_assert(!g_strcmp0(data, contents));
g_free(data);
g_key_file_unref(k);
remove(file);
remove(subdir);
remove(dir);
g_free(file);
g_free(dir);
g_free(subdir);
}
/* ==== merge_ignore ==== */
static void test_merge_ignore0(void)
{
static const char contents [] = "[foo]\na=1\nb=2\n";
char *dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
char *subdir = g_strconcat(dir, "/foo.d", NULL);
char *subdir2 = g_strconcat(subdir, "/dir.conf", NULL);
char *file = g_strconcat(dir, "/foo.conf", NULL);
GKeyFile *k = g_key_file_new();
char *data;
/* Two empty subdirectories, one with matching name, one not */
g_assert(!mkdir(subdir, 0700));
g_assert(!mkdir(subdir2, 0700));
g_assert(g_file_set_contents(file, contents, -1, NULL));
DBG("reading %s", file);
config_merge_files(k, file);
data = g_key_file_to_data(k, NULL, NULL);
DBG("\n%s", data);
g_assert(!g_strcmp0(data, contents));
g_free(data);
g_key_file_unref(k);
remove(file);
remove(subdir2);
remove(subdir);
remove(dir);
g_free(file);
g_free(dir);
g_free(subdir);
g_free(subdir2);
}
static void test_merge_ignore1(void)
{
static const char contents [] = "[foo]\na=1\nb=2\n";
static const char contents1 [] = "[foo]\nb=3\n";
/* File has no suffix */
test_merge_ignore("foo.conf", contents, "foo.d", "file", contents1);
}
static void test_merge_ignore2(void)
{
static const char contents [] = "[foo]\na=1\nb=2\n";
static const char contents1 [] = "[[[[[[[";
/* File is not a valid keyfile */
test_merge_ignore("foo.conf", contents, "foo.d", "a.conf", contents1);
}
/* ==== merge_sort ==== */
static void test_merge_sort(void)
{
static const char contents [] = "[foo]\na=1\nb=2\n";
static const char contents1 [] = "[foo]\nb=3\n";
static const char contents2 [] = "[foo]\nb=4\n";
static const char result [] = "[foo]\na=1\nb=4\n";
/* Test file sort order */
char *dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
char *file = g_strconcat(dir, "/foo.", NULL);
char *subdir = g_strconcat(dir, "/foo.d", NULL);
char *file1 = g_strconcat(subdir, "/1.conf", NULL);
char *file2 = g_strconcat(subdir, "/2.conf", NULL);
GKeyFile *k = g_key_file_new();
char *data;
g_assert(!mkdir(subdir, 0700));
g_assert(g_file_set_contents(file, contents, -1, NULL));
g_assert(g_file_set_contents(file1, contents1, -1, NULL));
g_assert(g_file_set_contents(file2, contents2, -1, NULL));
DBG("reading %s", file);
config_merge_files(k, file);
data = g_key_file_to_data(k, NULL, NULL);
DBG("\n%s", data);
g_assert(!g_strcmp0(data, result));
g_free(data);
g_key_file_unref(k);
remove(file);
remove(file1);
remove(file2);
remove(subdir);
remove(dir);
g_free(file);
g_free(file1);
g_free(file2);
g_free(dir);
g_free(subdir);
}
/* ==== merge_remove_group ==== */
static void test_merge_remove_group(void)
{
static const char contents [] = "[foo]\na=1\n\n[bar]\nb=1\n";
static const char contents1 [] = "[!bar]\n";
static const char result [] = "[foo]\na=1\n";
test_merge1(contents, contents1, result);
}
/* ==== merge_remove_key ==== */
static void test_merge_remove_key(void)
{
static const char contents [] = "[foo]\na=1\nb=2\n";
static const char contents1 [] = "[foo]\n!b=\n\n!=\n";
static const char result [] = "[foo]\na=1\n";
test_merge1(contents, contents1, result);
}
/* ==== merge_default_value ==== */
static void test_merge_default_value(void)
{
/* b is assigned the default value, a stays as is */
static const char contents [] = "[foo]\na=1\n";
static const char contents1 [] = "[foo]\na:=2\nb:=3\n";
static const char result [] = "[foo]\na=1\nb=3\n";
test_merge1(contents, contents1, result);
}
/* ==== merge_list_add ==== */
static void test_merge_list_add0(void)
{
/* Adding empty list */
static const char contents [] = "[foo]\na=1\nb=2\n";
static const char contents1 [] = "[foo]\na+=\n";
test_merge1(contents, contents1, contents);
}
static void test_merge_list_add1(void)
{
/* a=1 turns into a=1,2, */
static const char contents [] = "[foo]\na=1\nb=2\n";
static const char contents1 [] = "[foo]\na+=2,\n";
static const char result [] = "[foo]\na=1,2,\nb=2\n";
test_merge1(contents, contents1, result);
}
static void test_merge_list_add2(void)
{
/* 2 is already there */
static const char contents [] = "[foo]\na=1,2,\nb=2\n";
static const char contents1 [] = "[foo]\na?=2\n";
test_merge1(contents, contents1, contents);
}
static void test_merge_list_add3(void)
{
/* 2 is already there, 3 is not */
static const char contents [] = "[foo]\na=1,2,\n";
static const char contents1 [] = "[foo]\na?=2,3,\n";
static const char result [] = "[foo]\na=1,2,3,\n";
test_merge1(contents, contents1, result);
}
static void test_merge_list_add4(void)
{
/* b=2,3, is created */
static const char contents [] = "[foo]\na=1\n";
static const char contents1 [] = "[foo]\nb?=2,3,\n";
static const char result [] = "[foo]\na=1\nb=2,3,\n";
test_merge1(contents, contents1, result);
}
static void test_merge_list_add5(void)
{
/* Add a new group */
static const char contents [] = "[foo]\na=1\n";
static const char contents1 [] = "[bar]\nb=2\n";
static const char result [] = "[foo]\na=1\n\n[bar]\nb=2\n";
test_merge1(contents, contents1, result);
}
/* ==== merge_list_remove ==== */
static void test_merge_list_remove0(void)
{
static const char contents [] = "[foo]\na=1,2,\n";
static const char contents1 [] = "[foo]\na-=\n";
test_merge1(contents, contents1, contents);
}
static void test_merge_list_remove1(void)
{
static const char contents [] = "[foo]\na=1,2,\n";
static const char contents1 [] = "[foo]\na-=2,\n";
static const char result [] = "[foo]\na=1,\n";
test_merge1(contents, contents1, result);
}
static void test_merge_list_remove2(void)
{
static const char contents [] = "[foo]\na=1,2,\n";
static const char contents1 [] = "[foo]\na-=3\n";
test_merge1(contents, contents1, contents);
}
static void test_merge_list_remove3(void)
{
static const char contents [] = "[foo]\na=1,2,\n";
static const char contents1 [] = "[foo]\nb-=1\n";
test_merge1(contents, contents1, contents);
}
#define TEST_(name) "/config/" name
int main(int argc, char *argv[])
{
g_test_init(&argc, &argv, NULL);
__ofono_log_init("test-config",
g_test_verbose() ? "*" : NULL,
FALSE, FALSE);
g_test_add_func(TEST_("merge_basic"), test_merge_basic);
g_test_add_func(TEST_("merge_simple"), test_merge_simple);
g_test_add_func(TEST_("merge_empty_dir"), test_merge_empty_dir);
g_test_add_func(TEST_("merge_ignore0"), test_merge_ignore0);
g_test_add_func(TEST_("merge_ignore1"), test_merge_ignore1);
g_test_add_func(TEST_("merge_ignore2"), test_merge_ignore2);
g_test_add_func(TEST_("merge_sort"), test_merge_sort);
g_test_add_func(TEST_("merge_remove_group"), test_merge_remove_group);
g_test_add_func(TEST_("merge_remove_key"), test_merge_remove_key);
g_test_add_func(TEST_("merge_default_value"), test_merge_default_value);
g_test_add_func(TEST_("merge_list_add0"), test_merge_list_add0);
g_test_add_func(TEST_("merge_list_add1"), test_merge_list_add1);
g_test_add_func(TEST_("merge_list_add2"), test_merge_list_add2);
g_test_add_func(TEST_("merge_list_add3"), test_merge_list_add3);
g_test_add_func(TEST_("merge_list_add4"), test_merge_list_add4);
g_test_add_func(TEST_("merge_list_add5"), test_merge_list_add5);
g_test_add_func(TEST_("merge_list_remove0"), test_merge_list_remove0);
g_test_add_func(TEST_("merge_list_remove1"), test_merge_list_remove1);
g_test_add_func(TEST_("merge_list_remove2"), test_merge_list_remove2);
g_test_add_func(TEST_("merge_list_remove3"), test_merge_list_remove3);
return g_test_run();
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -1,7 +1,8 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2019 Jolla Ltd.
* Copyright (C) 2019-2020 Jolla Ltd.
* Copyright (C) 2020 Open Mobile Platform LLC.
*
* 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
@@ -103,6 +104,9 @@ static const struct test_method_name_data method_name_tests[] = {
},{
OFONO_DBUS_ACCESS_INTF_RADIOSETTINGS,
OFONO_DBUS_ACCESS_RADIOSETTINGS_METHOD_COUNT
},{
OFONO_DBUS_ACCESS_INTF_STK,
OFONO_DBUS_ACCESS_STK_METHOD_COUNT
}
};

View File

@@ -1,7 +1,8 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2018 Jolla Ltd.
* Copyright (C) 2018-2019 Jolla Ltd.
* Copyright (C) 2019 Open Mobile Platform LLC.
*
* 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
@@ -47,80 +48,6 @@ static void test_get_value(const char *conf, void (*test)(GKeyFile *k))
g_free(dir);
}
static gboolean test_keyfile_empty(GKeyFile *k)
{
gsize n = 0;
char **groups = g_key_file_get_groups(k, &n);
g_strfreev(groups);
return !n;
}
static void test_merge_ignore(const char *filename, const char *contents,
const char *dirname, const char *filename1, const char *contents1)
{
char *dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
char *file = g_strconcat(dir, "/", filename, NULL);
char *subdir = g_strconcat(dir, "/", dirname, NULL);
char *file1 = g_strconcat(subdir, "/", filename1, NULL);
GKeyFile *k = g_key_file_new();
char *data;
g_assert(!mkdir(subdir, 0700));
g_assert(g_file_set_contents(file, contents, -1, NULL));
g_assert(g_file_set_contents(file1, contents1, -1, NULL));
DBG("reading %s", file);
ril_config_merge_files(k, file);
data = g_key_file_to_data(k, NULL, NULL);
DBG("\n%s", data);
g_assert(!g_strcmp0(data, contents));
g_free(data);
g_key_file_unref(k);
remove(file);
remove(file1);
remove(subdir);
remove(dir);
g_free(file);
g_free(file1);
g_free(dir);
g_free(subdir);
}
static void test_merge1(const char *conf, const char *conf1, const char *out)
{
char *dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
char *file = g_strconcat(dir, "/foo.conf", NULL);
char *subdir = g_strconcat(dir, "/foo.d", NULL);
char *file1 = g_strconcat(subdir, "/bar.conf", NULL);
GKeyFile *k = g_key_file_new();
char *data;
g_assert(!mkdir(subdir, 0700));
g_assert(g_file_set_contents(file, conf, -1, NULL));
g_assert(g_file_set_contents(file1, conf1, -1, NULL));
DBG("reading %s", file);
g_key_file_set_list_separator(k, ',');
ril_config_merge_files(k, file);
data = g_key_file_to_data(k, NULL, NULL);
DBG("\n%s", data);
g_assert(!g_strcmp0(data, out));
g_free(data);
g_key_file_unref(k);
remove(file);
remove(file1);
remove(subdir);
remove(dir);
g_free(file);
g_free(file1);
g_free(dir);
g_free(subdir);
}
/* ==== get_string ==== */
static void test_get_string0_cb(GKeyFile *k)
@@ -500,311 +427,6 @@ static void test_ints_to_string(void)
g_assert(!ril_config_ints_to_string(NULL, 0));
}
/* ==== merge_basic ==== */
static void test_merge_basic(void)
{
GKeyFile *k = g_key_file_new();
char *nonexistent = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
ril_config_merge_files(NULL, NULL);
remove(nonexistent);
ril_config_merge_files(k, nonexistent);
g_assert(test_keyfile_empty(k));
ril_config_merge_files(k, NULL);
g_assert(test_keyfile_empty(k));
ril_config_merge_files(k, "");
g_assert(test_keyfile_empty(k));
g_key_file_unref(k);
g_free(nonexistent);
}
/* ==== merge_simple ==== */
static void test_merge_simple(void)
{
static const char contents [] = "[foo]\na=1\nb=2\n";
char *dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
char *file = g_strconcat(dir, "/foo.conf", NULL);
char *data;
GKeyFile *k = g_key_file_new();
g_assert(g_file_set_contents(file, contents, -1, NULL));
DBG("reading %s", file);
ril_config_merge_files(k, file);
data = g_key_file_to_data(k, NULL, NULL);
DBG("\n%s", data);
g_assert(!g_strcmp0(data, contents));
g_free(data);
g_key_file_unref(k);
remove(file);
remove(dir);
g_free(file);
g_free(dir);
}
/* ==== merge_empty_dir ==== */
static void test_merge_empty_dir(void)
{
static const char contents [] = "[foo]\na=1\nb=2\n";
char *dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
char *subdir = g_strconcat(dir, "/foo.d", NULL);
char *file = g_strconcat(dir, "/foo.conf", NULL);
GKeyFile *k = g_key_file_new();
char *data;
g_assert(!mkdir(subdir, 0700));
g_assert(g_file_set_contents(file, contents, -1, NULL));
DBG("reading %s", file);
ril_config_merge_files(k, file);
data = g_key_file_to_data(k, NULL, NULL);
DBG("\n%s", data);
g_assert(!g_strcmp0(data, contents));
g_free(data);
g_key_file_unref(k);
remove(file);
remove(subdir);
remove(dir);
g_free(file);
g_free(dir);
g_free(subdir);
}
/* ==== merge_ignore ==== */
static void test_merge_ignore0(void)
{
static const char contents [] = "[foo]\na=1\nb=2\n";
char *dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
char *subdir = g_strconcat(dir, "/foo.d", NULL);
char *subdir2 = g_strconcat(subdir, "/dir.conf", NULL);
char *file = g_strconcat(dir, "/foo.conf", NULL);
GKeyFile *k = g_key_file_new();
char *data;
/* Two empty subdirectories, one with matching name, one not */
g_assert(!mkdir(subdir, 0700));
g_assert(!mkdir(subdir2, 0700));
g_assert(g_file_set_contents(file, contents, -1, NULL));
DBG("reading %s", file);
ril_config_merge_files(k, file);
data = g_key_file_to_data(k, NULL, NULL);
DBG("\n%s", data);
g_assert(!g_strcmp0(data, contents));
g_free(data);
g_key_file_unref(k);
remove(file);
remove(subdir2);
remove(subdir);
remove(dir);
g_free(file);
g_free(dir);
g_free(subdir);
g_free(subdir2);
}
static void test_merge_ignore1(void)
{
static const char contents [] = "[foo]\na=1\nb=2\n";
static const char contents1 [] = "[foo]\nb=3\n";
/* File has no suffix */
test_merge_ignore("foo.conf", contents, "foo.d", "file", contents1);
}
static void test_merge_ignore2(void)
{
static const char contents [] = "[foo]\na=1\nb=2\n";
static const char contents1 [] = "[[[[[[[";
/* File is not a valid keyfile */
test_merge_ignore("foo.conf", contents, "foo.d", "a.conf", contents1);
}
/* ==== merge_sort ==== */
static void test_merge_sort(void)
{
static const char contents [] = "[foo]\na=1\nb=2\n";
static const char contents1 [] = "[foo]\nb=3\n";
static const char contents2 [] = "[foo]\nb=4\n";
static const char result [] = "[foo]\na=1\nb=4\n";
/* Test file sort order */
char *dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
char *file = g_strconcat(dir, "/foo.", NULL);
char *subdir = g_strconcat(dir, "/foo.d", NULL);
char *file1 = g_strconcat(subdir, "/1.conf", NULL);
char *file2 = g_strconcat(subdir, "/2.conf", NULL);
GKeyFile *k = g_key_file_new();
char *data;
g_assert(!mkdir(subdir, 0700));
g_assert(g_file_set_contents(file, contents, -1, NULL));
g_assert(g_file_set_contents(file1, contents1, -1, NULL));
g_assert(g_file_set_contents(file2, contents2, -1, NULL));
DBG("reading %s", file);
ril_config_merge_files(k, file);
data = g_key_file_to_data(k, NULL, NULL);
DBG("\n%s", data);
g_assert(!g_strcmp0(data, result));
g_free(data);
g_key_file_unref(k);
remove(file);
remove(file1);
remove(file2);
remove(subdir);
remove(dir);
g_free(file);
g_free(file1);
g_free(file2);
g_free(dir);
g_free(subdir);
}
/* ==== merge_remove_group ==== */
static void test_merge_remove_group(void)
{
static const char contents [] = "[foo]\na=1\n\n[bar]\nb=1\n";
static const char contents1 [] = "[!bar]\n";
static const char result [] = "[foo]\na=1\n";
test_merge1(contents, contents1, result);
}
/* ==== merge_remove_key ==== */
static void test_merge_remove_key(void)
{
static const char contents [] = "[foo]\na=1\nb=2\n";
static const char contents1 [] = "[foo]\n!b=\n\n!=\n";
static const char result [] = "[foo]\na=1\n";
test_merge1(contents, contents1, result);
}
/* ==== merge_default_value ==== */
static void test_merge_default_value(void)
{
/* b is assigned the default value, a stays as is */
static const char contents [] = "[foo]\na=1\n";
static const char contents1 [] = "[foo]\na:=2\nb:=3\n";
static const char result [] = "[foo]\na=1\nb=3\n";
test_merge1(contents, contents1, result);
}
/* ==== merge_list_add ==== */
static void test_merge_list_add0(void)
{
/* Adding empty list */
static const char contents [] = "[foo]\na=1\nb=2\n";
static const char contents1 [] = "[foo]\na+=\n";
test_merge1(contents, contents1, contents);
}
static void test_merge_list_add1(void)
{
/* a=1 turns into a=1,2, */
static const char contents [] = "[foo]\na=1\nb=2\n";
static const char contents1 [] = "[foo]\na+=2,\n";
static const char result [] = "[foo]\na=1,2,\nb=2\n";
test_merge1(contents, contents1, result);
}
static void test_merge_list_add2(void)
{
/* 2 is already there */
static const char contents [] = "[foo]\na=1,2,\nb=2\n";
static const char contents1 [] = "[foo]\na?=2\n";
test_merge1(contents, contents1, contents);
}
static void test_merge_list_add3(void)
{
/* 2 is already there, 3 is not */
static const char contents [] = "[foo]\na=1,2,\n";
static const char contents1 [] = "[foo]\na?=2,3,\n";
static const char result [] = "[foo]\na=1,2,3,\n";
test_merge1(contents, contents1, result);
}
static void test_merge_list_add4(void)
{
/* b=2,3, is created */
static const char contents [] = "[foo]\na=1\n";
static const char contents1 [] = "[foo]\nb?=2,3,\n";
static const char result [] = "[foo]\na=1\nb=2,3,\n";
test_merge1(contents, contents1, result);
}
static void test_merge_list_add5(void)
{
/* Add a new group */
static const char contents [] = "[foo]\na=1\n";
static const char contents1 [] = "[bar]\nb=2\n";
static const char result [] = "[foo]\na=1\n\n[bar]\nb=2\n";
test_merge1(contents, contents1, result);
}
/* ==== merge_list_remove ==== */
static void test_merge_list_remove0(void)
{
static const char contents [] = "[foo]\na=1,2,\n";
static const char contents1 [] = "[foo]\na-=\n";
test_merge1(contents, contents1, contents);
}
static void test_merge_list_remove1(void)
{
static const char contents [] = "[foo]\na=1,2,\n";
static const char contents1 [] = "[foo]\na-=2,\n";
static const char result [] = "[foo]\na=1,\n";
test_merge1(contents, contents1, result);
}
static void test_merge_list_remove2(void)
{
static const char contents [] = "[foo]\na=1,2,\n";
static const char contents1 [] = "[foo]\na-=3\n";
test_merge1(contents, contents1, contents);
}
static void test_merge_list_remove3(void)
{
static const char contents [] = "[foo]\na=1,2,\n";
static const char contents1 [] = "[foo]\nb-=1\n";
test_merge1(contents, contents1, contents);
}
#define TEST_(name) "/ril_config/" name
int main(int argc, char *argv[])
@@ -831,26 +453,6 @@ int main(int argc, char *argv[])
g_test_add_func(TEST_("get_enum"), test_get_enum);
g_test_add_func(TEST_("get_ints"), test_get_ints);
g_test_add_func(TEST_("ints_to_string"), test_ints_to_string);
g_test_add_func(TEST_("merge_basic"), test_merge_basic);
g_test_add_func(TEST_("merge_simple"), test_merge_simple);
g_test_add_func(TEST_("merge_empty_dir"), test_merge_empty_dir);
g_test_add_func(TEST_("merge_ignore0"), test_merge_ignore0);
g_test_add_func(TEST_("merge_ignore1"), test_merge_ignore1);
g_test_add_func(TEST_("merge_ignore2"), test_merge_ignore2);
g_test_add_func(TEST_("merge_sort"), test_merge_sort);
g_test_add_func(TEST_("merge_remove_group"), test_merge_remove_group);
g_test_add_func(TEST_("merge_remove_key"), test_merge_remove_key);
g_test_add_func(TEST_("merge_default_value"), test_merge_default_value);
g_test_add_func(TEST_("merge_list_add0"), test_merge_list_add0);
g_test_add_func(TEST_("merge_list_add1"), test_merge_list_add1);
g_test_add_func(TEST_("merge_list_add2"), test_merge_list_add2);
g_test_add_func(TEST_("merge_list_add3"), test_merge_list_add3);
g_test_add_func(TEST_("merge_list_add4"), test_merge_list_add4);
g_test_add_func(TEST_("merge_list_add5"), test_merge_list_add5);
g_test_add_func(TEST_("merge_list_remove0"), test_merge_list_remove0);
g_test_add_func(TEST_("merge_list_remove1"), test_merge_list_remove1);
g_test_add_func(TEST_("merge_list_remove2"), test_merge_list_remove2);
g_test_add_func(TEST_("merge_list_remove3"), test_merge_list_remove3);
return g_test_run();
}

View File

@@ -0,0 +1,326 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2019 Jolla Ltd.
* Copyright (C) 2019 Open Mobile Platform LLC.
*
* 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 "drivers/ril/ril_ecclist.h"
#include "drivers/ril/ril_log.h"
#include "ofono.h"
#include <gutil_strv.h>
#include <sys/stat.h>
#define TMP_DIR_TEMPLATE "test-ril_ecclist-XXXXXX"
#define TEST_TIMEOUT_SEC (20)
GLOG_MODULE_DEFINE("rilmodem");
static gboolean test_debug = FALSE;
struct ril_ecclist_parse_test {
const char* name;
const char* in;
const char* const* out;
};
static gboolean test_timeout_cb(gpointer user_data)
{
g_assert_not_reached();
return G_SOURCE_REMOVE;
}
static gboolean test_idle_quit_cb(gpointer loop)
{
g_main_loop_quit(loop);
return G_SOURCE_REMOVE;
}
static void test_quit_cb(struct ril_ecclist *ecc, gpointer loop)
{
g_idle_add(test_idle_quit_cb, loop);
}
static void test_inc_cb(struct ril_ecclist *ecc, gpointer ptr)
{
(*(int*)ptr)++;
}
/* ==== parse ==== */
static void test_parse(gconstpointer data)
{
const struct ril_ecclist_parse_test *test = data;
char *dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
char *file = g_build_filename(dir, "ecclist", NULL);
struct ril_ecclist *ecc;
GDEBUG("Created file %s", file);
g_assert(g_file_set_contents(file, test->in, -1, NULL));
ecc = ril_ecclist_new(file);
g_assert(gutil_strv_equal(ecc->list, (char**)test->out));
ril_ecclist_unref(ecc);
remove(file);
remove(dir);
g_free(file);
g_free(dir);
}
static const char* null_str = NULL;
static const char single_str_in[] = "911";
static const char* single_str_out[] = { "911", NULL };
static const char double_str_in[] = "911,112";
static const char double2_str_in[] = "911, 112,";
static const char double3_str_in[] = "911, 911, 112 ";
static const char* double_str_out[] = { "112", "911", NULL };
static const char mtk_str_in[] = "112,31;911,31;112,-1;911,-1";
static const char mtk2_str_in[] = "112,31; 911,31; 112; 911 ";
static const struct ril_ecclist_parse_test tests[] = {
{ "empty", "", &null_str },
{ "single", single_str_in, single_str_out },
{ "double", double_str_in, double_str_out },
{ "double2", double2_str_in, double_str_out },
{ "double3", double3_str_in, double_str_out },
{ "mtk", mtk_str_in, double_str_out },
{ "mtk2", mtk2_str_in, double_str_out }
};
/* ==== file_perm ==== */
static void test_file_perm()
{
char *dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
char *file = g_build_filename(dir, "ecclist", NULL);
int count = 0;
struct ril_ecclist *ecc;
gulong id[2];
GMainLoop *loop = g_main_loop_new(NULL, FALSE);
guint test_timeout_id = test_debug ? 0 :
g_timeout_add_seconds(TEST_TIMEOUT_SEC, test_timeout_cb, NULL);
GDEBUG("Created file %s", file);
g_assert(g_file_set_contents(file, single_str_in, -1, NULL));
ecc = ril_ecclist_new(file);
id[0] = ril_ecclist_add_list_changed_handler(ecc, test_inc_cb, &count);
id[1] = ril_ecclist_add_list_changed_handler(ecc, test_quit_cb, loop);
g_assert(id[0]);
g_assert(id[1]);
g_assert(gutil_strv_equal(ecc->list, (char**)single_str_out));
/* Modify the file */
g_assert(g_file_set_contents(file, double_str_in, -1, NULL));
/* ril_ecclist needs event loop to process filesystem change events */
g_main_loop_run(loop);
g_assert(count == 1);
g_assert(gutil_strv_equal(ecc->list, (char**)double_str_out));
/* Making file unreadable resets the ecc list */
GDEBUG("Making file %s unreadable", file);
g_assert(g_file_set_contents(file, single_str_in, -1, NULL));
g_assert(chmod(file, 0) == 0);
count = 0;
g_main_loop_run(loop);
g_assert(count == 1);
g_assert(!ecc->list);
if (test_timeout_id) {
g_source_remove(test_timeout_id);
}
g_main_loop_unref(loop);
ril_ecclist_remove_handler(ecc, id[0]);
ril_ecclist_remove_handler(ecc, id[1]);
ril_ecclist_unref(ecc);
remove(dir);
g_free(file);
g_free(dir);
}
/* ==== file_change ==== */
static void test_file_change()
{
char *dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
char *file = g_build_filename(dir, "ecclist", NULL);
int count = 0;
struct ril_ecclist *ecc;
gulong id[2];
GMainLoop *loop = g_main_loop_new(NULL, FALSE);
guint test_timeout_id = test_debug ? 0 :
g_timeout_add_seconds(TEST_TIMEOUT_SEC, test_timeout_cb, NULL);
GDEBUG("Created file %s", file);
g_assert(g_file_set_contents(file, single_str_in, -1, NULL));
ecc = ril_ecclist_new(file);
id[0] = ril_ecclist_add_list_changed_handler(ecc, test_inc_cb, &count);
id[1] = ril_ecclist_add_list_changed_handler(ecc, test_quit_cb, loop);
g_assert(id[0]);
g_assert(id[1]);
g_assert(gutil_strv_equal(ecc->list, (char**)single_str_out));
/* Modify the file */
g_assert(g_file_set_contents(file, double_str_in, -1, NULL));
/* ril_ecclist needs event loop to process filesystem change events */
g_main_loop_run(loop);
g_assert(count == 1);
g_assert(gutil_strv_equal(ecc->list, (char**)double_str_out));
/* Removing the file resets the ecc list */
GDEBUG("Removing file %s", file);
g_assert(remove(file) == 0);
count = 0;
g_main_loop_run(loop);
g_assert(count == 1);
g_assert(!ecc->list);
if (test_timeout_id) {
g_source_remove(test_timeout_id);
}
g_main_loop_unref(loop);
ril_ecclist_remove_handler(ecc, id[0]);
ril_ecclist_remove_handler(ecc, id[1]);
ril_ecclist_unref(ecc);
remove(dir);
g_free(file);
g_free(dir);
}
/* ==== dir_change ==== */
static void test_dir_change()
{
char *dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
char *file = g_build_filename(dir, "ecclist", NULL);
int count = 0;
struct ril_ecclist *ecc;
gulong id[3];
GMainLoop *loop = g_main_loop_new(NULL, FALSE);
guint test_timeout_id = test_debug ? 0 :
g_timeout_add_seconds(TEST_TIMEOUT_SEC, test_timeout_cb, NULL);
GDEBUG("Created directory %s", dir);
ecc = ril_ecclist_new(file);
id[0] = ril_ecclist_add_list_changed_handler(ecc, test_inc_cb, &count);
id[1] = ril_ecclist_add_list_changed_handler(ecc, test_quit_cb, loop);
g_assert(id[0]);
g_assert(id[1]);
g_assert(!ecc->list);
GDEBUG("Created file %s", file);
g_assert(g_file_set_contents(file, single_str_in, -1, NULL));
/* ril_ecclist needs event loop to process filesystem change events */
g_main_loop_run(loop);
g_assert(count == 1);
g_assert(gutil_strv_equal(ecc->list, (char**)single_str_out));
/* Removing the directory resets the ecc list */
GDEBUG("Removing directory %s", dir);
g_assert(remove(file) == 0);
g_assert(remove(dir) == 0);
count = 0;
g_main_loop_run(loop);
g_assert(count == 1);
g_assert(!ecc->list);
if (test_timeout_id) {
g_source_remove(test_timeout_id);
}
g_main_loop_unref(loop);
ril_ecclist_remove_handler(ecc, id[0]);
ril_ecclist_remove_handler(ecc, id[1]);
ril_ecclist_unref(ecc);
g_free(file);
g_free(dir);
}
/* ==== null ==== */
static void test_null(void)
{
char *dir = g_dir_make_tmp(TMP_DIR_TEMPLATE, NULL);
char *file = g_build_filename(dir, "ecclist", NULL);
struct ril_ecclist *ecc;
/* Make sure neither directory nor file exist */
remove(dir);
ecc = ril_ecclist_new(file);
g_assert(ecc);
g_assert(!ecc->list);
g_assert(!ril_ecclist_new(NULL));
g_assert(!ril_ecclist_ref(NULL));
g_assert(!ril_ecclist_add_list_changed_handler(NULL, NULL, NULL));
g_assert(!ril_ecclist_add_list_changed_handler(ecc, NULL, NULL));
ril_ecclist_unref(NULL);
ril_ecclist_remove_handler(NULL, 0);
ril_ecclist_remove_handler(ecc, 0);
ril_ecclist_unref(ril_ecclist_ref(ecc));
ril_ecclist_unref(ecc);
g_free(file);
g_free(dir);
}
#define TEST_(name) "/ril_ecclist/" name
int main(int argc, char *argv[])
{
int i;
g_test_init(&argc, &argv, NULL);
gutil_log_timestamp = FALSE;
gutil_log_default.name = "test-ril_ecclist";
gutil_log_default.level = g_test_verbose() ?
GLOG_LEVEL_VERBOSE : GLOG_LEVEL_NONE;
__ofono_log_init(gutil_log_default.name,
g_test_verbose() ? "*" : NULL,
FALSE, FALSE);
if (argc > 1 && !strcmp(argv[1] , "-d")) {
test_debug = TRUE;
GDEBUG("Debugging on (no timeout)");
}
for (i = 0; i < G_N_ELEMENTS(tests); i++) {
const struct ril_ecclist_parse_test* test = tests + i;
char* path = g_strconcat(TEST_("parse/"), test->name, NULL);
g_test_add_data_func(path, test, test_parse);
g_free(path);
}
g_test_add_func(TEST_("null"), test_null);
g_test_add_func(TEST_("file_perm"), test_file_perm);
g_test_add_func(TEST_("file_change"), test_file_change);
g_test_add_func(TEST_("dir_change"), test_dir_change);
return g_test_run();
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -1,7 +1,8 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2017-2019 Jolla Ltd.
* Copyright (C) 2017-2020 Jolla Ltd.
* Copyright (C) 2019-2020 Open Mobile Platform LLC.
*
* 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
@@ -46,6 +47,7 @@
#define TEST_SPN "Test"
#define TEST_ERROR_KEY "Error"
#define TEST_SLOT_ERROR_KEY "SlotError"
#define TEST_CONFIG_DIR_TEMPLATE "test-saifish_manager-config-XXXXXX"
extern struct ofono_plugin_desc __ofono_builtin_sailfish_manager;
static GMainLoop *test_loop = NULL;
@@ -970,6 +972,128 @@ static void test_voice_sim(void)
test_common_deinit();
}
/* ==== auto_data_sim ==== */
static gboolean test_auto_data_sim_done(gpointer user_data)
{
test_slot_manager *sm = user_data;
test_slot *s = sm->slot;
struct sailfish_manager *m = fake_sailfish_manager_dbus.m;
struct ofono_watch *w = ofono_watch_new(TEST_PATH);
struct ofono_watch *w2 = ofono_watch_new(TEST_PATH_1);
struct ofono_modem modem;
struct ofono_sim sim;
struct ofono_sim sim2;
memset(&modem, 0, sizeof(modem));
memset(&sim, 0, sizeof(sim));
sim.mcc = TEST_MCC;
sim.mnc = TEST_MNC;
sim.state = OFONO_SIM_STATE_READY;
sim2 = sim;
/* Assign IMSI to the SIMs */
w->modem = &modem;
fake_watch_signal_queue(w, FAKE_WATCH_SIGNAL_MODEM_CHANGED);
fake_watch_set_ofono_sim(w, &sim);
fake_watch_set_ofono_iccid(w, TEST_ICCID);
fake_watch_set_ofono_imsi(w, TEST_IMSI);
fake_watch_emit_queued_signals(w);
w2->modem = &modem;
fake_watch_signal_queue(w2, FAKE_WATCH_SIGNAL_MODEM_CHANGED);
fake_watch_set_ofono_sim(w2, &sim2);
fake_watch_set_ofono_iccid(w2, TEST_ICCID_1);
fake_watch_set_ofono_imsi(w2, TEST_IMSI_1);
fake_watch_emit_queued_signals(w2);
/* No data SIM yet, only voice SIM is assigned */
g_assert(s->data_role == SAILFISH_DATA_ROLE_NONE);
g_assert(!m->default_voice_imsi);
g_assert(!g_strcmp0(m->default_voice_path, TEST_PATH));
g_assert(!m->default_data_imsi);
g_assert(!m->default_data_path);
/* Set the first modem online */
w->online = TRUE;
fake_watch_signal_queue(w, FAKE_WATCH_SIGNAL_ONLINE_CHANGED);
fake_watch_emit_queued_signals(w);
/* Now data modem must point to the first slot */
g_assert(!g_strcmp0(m->default_data_path, TEST_PATH));
ofono_watch_unref(w);
ofono_watch_unref(w2);
g_main_loop_quit(test_loop);
return G_SOURCE_REMOVE;
}
static guint test_auto_data_sim_start(test_slot_manager *sm)
{
struct sailfish_manager *m = fake_sailfish_manager_dbus.m;
test_slot *s = g_new0(test_slot, 1);
test_slot *s2 = g_new0(test_slot, 1);
DBG("");
/* Create the slots */
DBG("");
s->handle = sailfish_manager_slot_add(sm->handle, s, TEST_PATH,
OFONO_RADIO_ACCESS_MODE_GSM, NULL, TEST_IMEISV,
SAILFISH_SIM_STATE_PRESENT);
s2->handle = sailfish_manager_slot_add(sm->handle, s2, TEST_PATH_1,
OFONO_RADIO_ACCESS_MODE_GSM, NULL, TEST_IMEISV,
SAILFISH_SIM_STATE_PRESENT);
sm->slot = s;
sm->slot2 = s2;
sailfish_slot_manager_started(sm->handle);
g_assert(!m->ready);
sailfish_manager_imei_obtained(s->handle, TEST_IMEI);
g_assert(!m->ready);
sailfish_manager_imei_obtained(s2->handle, TEST_IMEI_1);
g_assert(m->ready);
g_idle_add(test_auto_data_sim_done, sm);
return 0;
}
static void test_auto_data_sim(gconstpointer option)
{
static const struct sailfish_slot_driver test_auto_data_sim_driver = {
.name = "auto_data_sim",
.manager_create = test_slot_manager_create,
.manager_start = test_auto_data_sim_start,
.manager_free = test_slot_manager_free,
.slot_enabled_changed = test_slot_enabled_changed,
.slot_free = test_slot_free
};
char *cfg_dir = g_dir_make_tmp(TEST_CONFIG_DIR_TEMPLATE, NULL);
char *cfg_file = g_build_filename(cfg_dir, "main.conf", NULL);
GKeyFile* cfg = g_key_file_new();
struct sailfish_slot_driver_reg *reg;
g_key_file_set_string(cfg, "ModemManager", "AutoSelectDataSim", option);
g_assert(g_key_file_save_to_file(cfg, cfg_file, NULL));
g_key_file_unref(cfg);
__ofono_set_config_dir(cfg_dir);
test_common_init();
reg = sailfish_slot_driver_register(&test_auto_data_sim_driver);
g_assert(reg);
g_main_loop_run(test_loop);
sailfish_slot_driver_unregister(reg);
test_common_deinit();
__ofono_set_config_dir(NULL);
remove(cfg_file);
remove(cfg_dir);
g_free(cfg_file);
g_free(cfg_dir);
}
/* ==== data_sim ==== */
static gboolean test_data_sim_done(gpointer user_data)
@@ -1066,8 +1190,17 @@ static void test_data_sim(void)
.slot_enabled_changed = test_slot_enabled_changed,
.slot_free = test_slot_free
};
char *cfg_dir = g_dir_make_tmp(TEST_CONFIG_DIR_TEMPLATE, NULL);
char *cfg_file = g_build_filename(cfg_dir, "main.conf", NULL);
GKeyFile* cfg = g_key_file_new();
struct sailfish_slot_driver_reg *reg;
/* Invalid AutoSelectDataSim option is treated as "off" */
g_key_file_set_string(cfg, "ModemManager", "AutoSelectDataSim", "x");
g_assert(g_key_file_save_to_file(cfg, cfg_file, NULL));
g_key_file_unref(cfg);
__ofono_set_config_dir(cfg_dir);
test_common_init();
reg = sailfish_slot_driver_register(&test_data_sim_driver);
g_assert(reg);
@@ -1076,6 +1209,12 @@ static void test_data_sim(void)
sailfish_slot_driver_unregister(reg);
test_common_deinit();
__ofono_set_config_dir(NULL);
remove(cfg_file);
remove(cfg_dir);
g_free(cfg_file);
g_free(cfg_dir);
}
/* ==== mms_sim ==== */
@@ -1511,6 +1650,12 @@ int main(int argc, char *argv[])
g_test_add_func(TEST_("cancel_start"), test_cancel_start);
g_test_add_func(TEST_("voice_sim"), test_voice_sim);
g_test_add_func(TEST_("data_sim"), test_data_sim);
g_test_add_data_func(TEST_("auto_data_sim_on"), "on",
test_auto_data_sim);
g_test_add_data_func(TEST_("auto_data_sim_always"), "always",
test_auto_data_sim);
g_test_add_data_func(TEST_("auto_data_sim_once"), "once",
test_auto_data_sim);
g_test_add_func(TEST_("mms_sim"), test_mms_sim);
g_test_add_func(TEST_("multisim"), test_multisim);
g_test_add_func(TEST_("storage"), test_storage);

View File

@@ -500,8 +500,8 @@ static void test_application_entry_decode(void)
g_assert(app[1]->label != NULL);
g_assert(!strcmp(app[1]->label, "MIDPfiles"));
g_free(ef_dir);
g_slist_free_full(entries, (GDestroyNotify) sim_app_record_free);
g_free(ef_dir);
}
static void test_get_3g_path(void)

View File

@@ -1,6 +1,6 @@
Name: ofono
Summary: Open Source Telephony
Version: 1.22
Version: 1.23
Release: 1
Group: Communications/Connectivity Adaptation
License: GPLv2