Compare commits

...

130 Commits

Author SHA1 Message Date
Slava Monich
2d18086c80 Merge branch 'sailfish_manager' into 'master'
Move Sailfish OS specific D-Bus interfaces to a separate plugin

See merge request !136
2017-08-22 12:15:27 +00:00
Slava Monich
0f4560c2eb [ofono] Moved RIL-independent stuff out of Sailfish OS RIL driver. JB#39257
The new sailfish_manager plugin contains the code necessary for
proper integration of ofono into Sailfish OS (mostly, that's
Sailfish OS specific D-Bus interfaces) and can be used by pretty
much any driver, not necessarily ril.
2017-08-22 12:07:01 +03:00
Slava Monich
7d80344d6b [ofono] network: Added ofono_netreg_get_name() function
Returns the name of the current operator.
2017-07-24 01:17:33 +03:00
Sergey Chupligin
c0c4148099 [ril] Added option to disable voicecall handling. Contributes to MER#1798 2017-07-21 15:34:39 +03:00
Slava Monich
34755f1a79 [ril] Housekeeping
Took out unnecessary include
2017-07-16 21:26:46 +03:00
Slava Monich
282d560c37 [ril] Changed the name of the common section in the error statistics file
Trying to get rid of RIL specific names, before it's too late.
2017-07-16 15:37:59 +03:00
Slava Monich
e4bca84876 [pushforwarder] Housekeeping
Removed unnecessary checks for memory allocation failures.

Replaced inotify stuff with a simpler version of the same thing -
since we already depend on libglibutil, there's no reason not to
use functions from there.
2017-07-15 12:54:15 +03:00
Slava Monich
aa0ded78b0 Merge branch 'slow_down' into 'master'
Serialize critical request sequences

See merge request !132
2017-07-13 18:40:45 +00:00
Slava Monich
eb15b12caf [ril] Don't start radio caps switch until SIM I/O calms down. JB#38689
That seems to significantly reduce the probability of a failure at startup
(we are reading the phonebook right after SIM interface is initialized)
2017-07-13 18:10:18 +03:00
Slava Monich
81b5c716e2 [ril] Notify the UI if the capability switch transaction fails. JB#38689
That bumps ModemManager protocol version to 8
2017-07-13 12:01:46 +03:00
Slava Monich
33c330988f [ril] Handle the case of one of the SIMs missing. JB#38689
LTE could be enabled for the slot that has no SIM card in it.
In that case we want to swap the capabilities so that LTE
becomes available to the slot that does have a SIM in it.
2017-07-12 19:04:26 +03:00
Slava Monich
910057a265 [ril] Make radio capability switch more reliable. JB#38689
1. Don't allow any other requests while radio caps switch is ongoing.
2. Wait for all other requests to complete before starting the switch
3. Deactivate data calls, disable data before the switch
4. Increased the timeouts
2017-07-12 17:39:40 +03:00
Slava Monich
19f0f8d96e [ril] If SET_UICC_SUBSCRIPTION times out, drop (rather than cancel) it. jb#38689
So that it gets removed from the list of pending requests and
doesn't block the next request.
2017-07-12 16:01:47 +03:00
Slava Monich
f1f3c17c4c [ril] Made PIN requests blocking. JB#38998 2017-07-12 15:58:05 +03:00
Slava Monich
29d891cbce [ril] Made SET_SUPP_SVC_NOTIFICATION blocking. JB#38998
To slow things down at startup. Some RILs just don't like it too fast.
2017-07-12 15:55:55 +03:00
Slava Monich
89fa0d5d6a [ril] Made GSM_GET_BROADCAST_SMS_CONFIG blocking. JB#38998 2017-07-12 15:53:12 +03:00
Slava Monich
c382d9f456 [ril] Poll data call state if DEACTIVATE_DATA_CALL request fails. JB#38998
Also exposed ril_request_allow_data_new and ril_request_deactivate_data_call_new
functions - those may need to be used outside of the ril_data module.
2017-07-12 15:53:01 +03:00
Slava Monich
b209b6bee6 [ril] Make RIL_REQUEST_RADIO_POWER blocking. JB#38998 2017-07-12 15:38:46 +03:00
Slava Monich
ee3323e98b [ril] Block requests while UICC subscription is in progress. JB#38998
Only allow GET_SIM_STATUS. This should make RIL happier.
2017-07-12 15:38:46 +03:00
Slava Monich
9200e387e1 [ril] Serialize requests at startup. JB#38998
Slowing things down at startup seems to make RIL happier.
2017-07-12 15:38:46 +03:00
Slava Monich
2bc58a7f6f Merge branch 'ussd' into 'master'
Switch USSD state from USER_ACTION to IDLE when a notification is received

See merge request !129
2017-06-22 07:40:18 +00:00
Slava Monich
35079b11fe [ofono] Switch USSD state from USER_ACTION to IDLE. Fixes JB#38992
When a USSD notification is received in the USER_ACTION state,
the state needs to be switched to IDLE. Some networks send 0
(no further user action required) after the response timeout
expires. That should result in the user input form getting
removed from the screen.
2017-06-21 14:57:11 +03:00
Slava Monich
955d5882b2 Merge branch 'octal' into 'master'
Workaround for broken MMS proxy IP address

See merge request !128
2017-06-20 08:14:28 +00:00
Slava Monich
2583fa99ce [ofono] Workaround for broken MMS proxy IP address. JB#38990
Some operators provide IP address of the MMS proxy
prepending zeros to each number shorter then 3 digits,
e.g. "192.168.094.023" instead of "192.168.94.23".
That may look nicer but it's actually wrong because
the numbers starting with zeros are interpreted as
octal numbers. In the example above 023 actually means
16 and 094 is not a valid number at all.

In addition to publishing these broken settings on their
web sites, some of the operators send them over the air,
in which case we can't even blame the user for entering
an invalid IP address. We better be prepared to deal with
those.

Since nobody in the world seems to be actually using the
octal notation to write an IP address, let's remove the
leading zeros if we find them in the host part of the MMS
proxy URL.
2017-06-19 18:47:20 +03:00
Slava Monich
b8bb15ce9c [ril] Fixed voicecall problem introduced by commit 351ac1e9. JB#38105 2017-06-17 18:53:16 +03:00
Slava Monich
cbf24c7b08 Merge branch 'auth_method' into 'master'
Add ANY and NONE authentication methods

See merge request !126
2017-06-17 14:13:16 +00:00
Slava Monich
a4c4d1526e [ofono] Reset authentication method together with other context settings. JB#38972 2017-06-16 17:38:32 +03:00
Slava Monich
18d1a8834a [rpm] Require mobile-broadband-provider-info at runtime
Even though it's not really required at build time anymore, let's still
keep it there because this requirement is hardcoded in configure.ac which
we pull from upstream.
2017-06-16 17:05:33 +03:00
Slava Monich
8343d96db5 [test] Improved provisioning test coverage
It no longer requires mobile-broadband-provider-info package to run
2017-06-16 17:04:28 +03:00
Slava Monich
415fce9368 [ofono] Allow to compile unit tests with coverage enabled
... and added unit/coverage script which runs the tests and
generates html report.

Code coverage has to be enabled with --enable-test-coverage
configure switch
2017-06-16 17:04:28 +03:00
Slava Monich
33257a139d [ril] Respect the authentication algorithm option. JB#38972 2017-06-16 17:04:28 +03:00
Slava Monich
f580867c12 [ofono] Added ANY and NONE authentication methods. JB#38972
ANY is supposed to allow either PAP or CHAP to be performed at plugin's
discretion. This is now the default (rather than CHAP)

NONE disables the authentication alltogether.
2017-06-16 17:03:40 +03:00
Slava Monich
edaba80ad1 Merge branch 'imeisv' into 'master'
Expose IMEI SV via org.nemomobile.ofono.ModemManager

See merge request !127
2017-06-16 09:07:49 +00:00
Slava Monich
e68314b07d [ofono] Expose IMEI SV via org.nemomobile.ofono.ModemManager. JB#38973
Unlike org.ofono.Modem properties, it's always going to be available
even for the slots that are disabled.
2017-06-15 15:30:03 +03:00
Slava Monich
d13e48b638 Merge branch 'radio_caps' into 'master'
Start using RIL_REQUEST_SET_RADIO_CAPABILITY

See merge request !122
2017-06-15 11:53:27 +00:00
Slava Monich
e0edfca358 [ril] Added SetRadioCapability config option. JB#38689
The RIL_REQUEST_SET_RADIO_CAPABILITY request is still not supported
by some relatively recent RILs, it should be possible to forcibly
turn it off.
2017-06-14 17:54:34 +03:00
Slava Monich
7cd2075ada [ril] Start using SET_RADIO_CAPABILITY. Fixes JB#38689
With some RILs it seems to be the only way to let all slots to use 4G,
SET_PREFERRED_NETWORK_TYPE is not always enough.
2017-06-14 17:50:51 +03:00
Slava Monich
3ccacfd5f7 [ril] Notify rild when we don't need mobile data. JB#38689
We were only telling rild when we need mobile data, but we should also
tell it when we don't need it.
2017-06-11 21:27:28 +03:00
Slava Monich
5ee13f8e2c [rpm] Drop the .changes file
It's not being maintained.
2017-06-11 16:09:09 +03:00
Slava Monich
6ab9dcb553 Merge branch 'sailfish_plugins' into 'master'
Consistent naming of Sailfish OS specific plugins

See merge request !125
2017-06-11 13:08:19 +00:00
Slava Monich
102061107a [ofono] push-forwarder -> sailfish-pushforwarder. Fixes JB#38927
The configure option --enable-sailfish-pushforwarder is now required in order
to enable this plugin (it was enabled by default)
2017-06-11 15:56:03 +03:00
Slava Monich
2bb7d629f5 [ofono] debuglog plugin -> sailfish_debuglog. JB#38927 2017-06-11 15:47:43 +03:00
Slava Monich
5ce01787e8 [ofono] Renamed jolla-rilmodem configure option into sailfish-rilmodem. JB#38927 2017-06-11 15:42:30 +03:00
Slava Monich
1d57cb0e73 [ofono] Renamed sailfishos configure option to sailfish-bt. JB#38927
Also, renamed sfos_bt.c into sailfish_bt.c so that the file name
looks similar to the configure option that enables it.
2017-06-11 15:41:56 +03:00
Slava Monich
3d84c0a120 [ofono] Renamed Sailfish OS specific provision plugin to sailfish_provision. JB#38927
plugins/provision.c is the original upstream provisioning plugin and
plugins/sailfish_provision.c is the Sailfish OS specific thing. They
are mutually exclusuve.

Sailfish OS specific plugin is enabled with --enable-sailfish-provision
configure switch. By default, the upstream plugin is used.
2017-06-11 15:40:57 +03:00
Slava Monich
091cf21c0b Merge branch 'hangup' into 'master'
Keep hangup D-Bus request pending until RIL hangup completes

See merge request !123
2017-06-08 09:58:44 +00:00
Slava Monich
351ac1e9db [ril] Keep hangup D-Bus request pending until RIL hangup completes. JB#38105 2017-06-07 16:49:57 +03:00
Slava Monich
b7481a918f radio-settings: Fix memory leaks in radio_load_settings
Errors returned by g_key_file_get_integer have to be deallocated
by the caller to avoid leaks like these:

==13330== 104 (24 direct, 80 indirect) bytes in 2 blocks are definitely lost
==13330==    at 0x483F3EC: malloc (vg_replace_malloc.c)
==13330==    by 0x4B020DF: g_malloc (gmem.c)
==13330==    by 0x4B17F51: g_slice_alloc (gslice.c)
==13330==    by 0x4AE80B9: g_error_new_valist (gerror.c)
==13330==    by 0x4AE830B: g_set_error (gerror.c)
==13330==    by 0x4AF5681: g_key_file_get_value (gkeyfile.c)
==13330==    by 0x4AF6817: g_key_file_get_integer (gkeyfile.c)
==13330==    by 0x10CFE3: radio_load_settings (radio-settings.c)
==13330==    by 0x10D2E3: ofono_radio_settings_register (radio-settings.c)
2017-06-02 21:23:23 +03:00
Slava Monich
e1e4381105 Merge branch 'default_data_sim' into 'master'
Don't auto-select the data sim on a multisim phone

See merge request !121
2017-05-31 07:59:32 +00:00
Slava Monich
cc3ca52e61 [ril] Don't auto-select the data sim on a multisim phone. JB#38719
This kind of behavior is reserved for single sim phone (for now).
2017-05-30 16:44:15 +03:00
Slava Monich
a71779ea2a [ril] Added assert in ril_sim_card_status_parse
.. that we have parsed the entire parcel to the end
2017-05-26 12:10:04 +03:00
Slava Monich
a9d2849bbb Merge branch 'jb38795' into 'master'
Fix passing -j to make

See merge request !120
2017-05-25 13:38:34 +00:00
Martin Kampas
ca29c8e538 [packaging] Fix passing -j to make. Contribute to JB#38795 2017-05-25 14:28:18 +02:00
Slava Monich
85fe1b7174 Merge branch 'imei' into 'master'
Use RIL_REQUEST_DEVICE_IDENTITY  instead of RIL_REQUEST_GET_IMEI

See merge request !117
2017-05-12 21:17:02 +00:00
Slava Monich
56e0d9dffa Merge branch 'file_watches' into 'master'
Prevent a crash in sim_fs_notify_file_watches

See merge request !118
2017-05-12 20:33:58 +00:00
Slava Monich
6867ba65cb [simfs] Prevent a crash in sim_fs_notify_file_watches. Fixes JB#38656
If no file watchers have ever been added, context->file_watches
is NULL and sim_fs_notify_file_watches() should take that into
account.
2017-05-12 18:51:53 +03:00
Slava Monich
6199eaa4d8 [ril] Allow (some) DEVICE_IDENTITY requests to time out. Fixes JB#38632
If GET_SIM_STATUS succeeds but DEVICE_IDENTITY keeps on failing,
allow the latter to time out. Some RILs behave that way until the
modem has been properly initialized.
2017-05-12 17:21:40 +03:00
Slava Monich
fabdd6799c [ril] Use DEVICE_IDENTITY request instead of GET_IMEI. Contributes to JB#38632
RIL_REQUEST_GET_IMEI has been deprecated since 2009
2017-05-12 17:15:01 +03:00
Slava Monich
1219ab6a3f Merge branch 'permlock' into 'master'
Detection of permanently locked SIM cards

See merge request !116
2017-04-28 16:13:23 +00:00
Slava Monich
85a956d9eb [ril] Improved detection of permanently locked SIM cards. JB#38257
If PUK is required, app state is PUK and pin1_state is ENABLED_BLOCKED.
If the card is permanently locked, app state is still PUK but pin1_state
becomes ENABLED_PERM_BLOCKED. That way we can tell whether the SIM card
is locked even if the number of remaining attempts is not available.
2017-04-28 16:06:22 +03:00
Slava Monich
c83d992a3b Merge branch 'retries' into 'master'
Query PUK retry count

See merge request !115
2017-04-28 11:54:09 +00:00
Slava Monich
b22027017c [ril] Query PUK retry count. JB#38257
PUK retry counts can be queried in a way similar to PIN retry counts
on those RILs that support it.
2017-04-27 21:36:46 +03:00
Slava Monich
1fa137b36d [sim] Allow plugins to use puk2pin() function 2017-04-27 21:27:27 +03:00
Slava Monich
cfd837b1db Merge branch 'puk' into 'master'
Always refresh SIM status from query_passwd_state

See merge request !114
2017-04-27 08:08:20 +00:00
Slava Monich
735ad21e89 [ril] Always refresh SIM status from query_passwd_state. Fixes JB#38257
After we have entered an invalid pin too many times, RIL signals
the SIM status change, we request the new status but ofono core
asks us for the new passwd state before our SIM status query has
completed. We need to wait for the query to complete before we can
report the new status to the core.

It also won't hurt if we request a fresh SIM status every time
when query_passwd_state callback is called, just in case if RIL
fails to notify us about the SIM status change.
2017-04-26 15:03:26 +03:00
Slava Monich
c9078404de [ril] Housekeeping 2017-04-14 19:51:42 +03:00
Slava Monich
e375195c92 Merge branch 'tech' into 'master'
Add "technologies" configuration option

See merge request !111
2017-04-10 21:10:22 +00:00
Slava Monich
ef5610f741 [ril] Added "technologies" configuration option. Fixes JB#38295
It supersedes enable4G option.
2017-04-05 21:18:00 +03:00
jpoutiai
aef9bbd3e0 Merge branch 'jb38053' into 'master'
[plugins] support bt call audio control. Fixes JB#38053

See merge request !110
2017-04-05 10:23:27 +00:00
Jarko Poutiainen
c6eb410f21 [plugins] support bt call audio control. Fixes JB#38053 2017-04-05 08:35:55 +03:00
Slava Monich
08b3ea3d0f Merge branch 'last_fail' into 'master'
Handle fancy variant of LAST_CALL_FAIL_CAUSE response

Some RILs get creative and invent their own formats.
They must be very proud of it.

See merge request !106
2017-03-15 21:46:11 +00:00
Slava Monich
2978862417 [ril] Handle fancy variant of LAST_CALL_FAIL_CAUSE response. Fixes JB#38079
Some RILs get creative and invent their own formats.
2017-03-15 17:19:37 +02:00
Slava Monich
19228c9e67 Merge branch 'lastcause' into 'master'
Allow to configure custom hangup reasons

One can define localHangupReasons and remoteHangupReasons in
ril_subscription.conf which will be treated as normal local or
remote hangup reasons.

See merge request !104
2017-02-24 13:11:46 +00:00
Slava Monich
9be791d531 [ofono] Allow to conifigure custom hangup reasons. Fixes JB#37879
One can define localHangupReasons and remoteHangupReasons in
ril_subscription.conf which will be treated as normal local or
remote hangup reasons. The value is a comma-separated list of
numbers, e.g.

localHangupReasons=20,39
2017-02-24 12:07:57 +02:00
Slava Monich
6b9eb7bf8f Merge branch 'voicecall' into 'master'
Don't use internal voicecall data structures

Added ofono_voicecall_find_call API instead

See merge request !105
2017-02-24 10:06:39 +00:00
Slava Monich
01f8989aee [ril] Don't use internal voicecall data structures
Use newly added ofono_voicecall_find_call API instead
2017-02-24 00:28:50 +02:00
Slava Monich
2f5efaf591 [ofono] Added ofono_voicecall_find_call API
For use by plugins
2017-02-24 00:28:18 +02:00
Slava Monich
ca1d06c37a Merge branch 'disable' into 'master'
Disable some unnecessary and harmful functionality

See merge request !103
2017-01-12 15:17:23 +00:00
Slava Monich
5f45928a84 [ofono] Disable PhoNet/ISI and QMI modem support. MER#1734
This makes ARM executable smaller by 170 KB
2017-01-12 00:47:04 +02:00
Slava Monich
19f74e6c85 [ofono] Don't allow to add or remove connection context over D-Bus. Fixes MER#1733
Quite a few things in SailfishOS assume that each modem has exactly
one internet and one mms context. However, ofono's D-Bus API allows
any application to arbitrarily add and remove connection contexts
which can screw things up quite badly. Since this functionality is
not used by SailfishOS, it should be disabled.
2017-01-12 00:27:17 +02:00
Slava Monich
41d5cfcab2 Merge branch 'modem-error' into 'master'
Count rild crashes

See merge request !102
2017-01-11 11:58:42 +00:00
Slava Monich
357c5db580 [ril] Count rild crashes. Contributes to JB#35780
org.nemomobile.ofono.ModemManager.ModemError signal is emitted
when rild crash is detected. Also, the new GetModemErrors method
allows to query how many times which instance of rild has crashed
since ofono was (re)started.
2017-01-11 13:24:23 +02:00
Slava Monich
8cea5b9f96 [ril] Allow to retry GET_BROADCAST_SMS_CONFIG. MER#1729
RIL_REQUEST_GSM_GET_BROADCAST_SMS_CONFIG sometimes fails at startup.
We may have to retry a few times (typically, once or twice on Jolla 1)
2017-01-05 18:40:50 +03:00
Slava Monich
5fb35d5fb4 Merge branch 'v1.19' into 'master'
Update ofono baseline to 1.19

It's the latest stable release.

See merge request !101
2017-01-05 12:39:13 +00:00
Slava Monich
e8d57bb928 [ofono] Update baseline to 1.19
Removed merge artifacts
2017-01-05 15:37:58 +03:00
Slava Monich
2bfde2418e [ril] Removed old workaround breaking hot-swap. Fixes MER#1730
The workaround is no longer needed. All SIM I/O requests have to
be completed even if the SIM card is removed while the request is
pending. Otherwise simfs request queue gets stuck.
2017-01-05 15:35:29 +03:00
Marcel Holtmann
c710ce76c1 Release 1.19 2017-01-05 11:20:08 +03:00
Alfonso Sánchez-Beato
78acd90464 gitignore: unit/test-rilmodem-gprs binary 2017-01-05 11:20:08 +03:00
Alfonso Sánchez-Beato
e51b3ca0c8 build: add rilmodem gprs unit tests
Conflicts:
	ofono/Makefile.am
2017-01-05 11:20:08 +03:00
Alfonso Sánchez-Beato
b3c8813bd4 unit: add rilmodem gprs tests
Add rilmodem gprs tests, which use the rilmodem test engine.
2017-01-05 11:20:08 +03:00
Alfonso Sánchez-Beato
e2a3acd9d0 unit: add rilmodem test engine
Add rilmodem test engine. This engine is an improvement on the rilmodem
test server that allows us to test generic interactions with the
rilmodem driver. Instead of just be able to check content of received/
sent bytes on the rild socket, we can now specify a set of steps for a
test that include interactions with the atom. The step types are

- TST_ACTION_SEND: The harness sends a parcel
- TST_ACTION_CALL: The harness calls a driver function
- TST_EVENT_RECEIVE: The driver sends a parcel
- TST_EVENT_CALL: The driver calls a harness (atom) function
2017-01-05 11:20:08 +03:00
Slava Monich
2054ca9570 include: Remove unused field from primary_context 2017-01-05 11:20:08 +03:00
Denis Kenzior
48dbb7912a AUTHORS: Mention Antoine's contributions 2017-01-05 11:20:08 +03:00
Denis Kenzior
d7e7ad671d gatchat: Fix style 2017-01-05 11:20:08 +03:00
Antoine Aubert
5b5a86dc80 gatmux: fix read channel remove on error
In case of invalid IO, read_watch is not reset. This fix crash on
destroy gatmux.
2017-01-05 11:20:08 +03:00
Slava Monich
c232524e99 main: Make -d option repeatable
Concatenating the patterns makes more sense than using the last
supplied value and leaking the previous allocated patterns.
2017-01-05 11:20:08 +03:00
Frédéric Dalleau
bfd09a5c14 udevng: Detect huawei E3372 modem and pcui
When the modes are modified with AT^SETPORT="FF;12,1,16", this modem
and pcui will appear using different ids. Update the interfaces
identifier accordingly.
2017-01-05 11:20:08 +03:00
Frédéric Dalleau
f8adcd2550 udevng: Detect huawei E3372 pcui
The huawei E3372 exposes two USB tty at /dev/ttyUSB0 and /dev/ttyUSB1
/dev/ttyUSB1 is properly detected as modem.
/dev/ttyUSB0 is the pcui.

lsusb shows 12d1:1506 Huawei Technologies Co., Ltd. Modem/Networkcard
2017-01-05 11:20:08 +03:00
Denis Kenzior
2a97567147 handsfree-audio: Fix crash
For HFP 1.5 devices the driver is always set to NULL.  So if the AG
establishes the SCO connection first, we might crash.
2017-01-05 11:20:07 +03:00
Santtu Lakkala
04d84b615e huaweimodem: Fix DHCP parsing on 32-bit platforms
strtol clamps returned value between INT_MIN and INT_MAX, causing
invalid value (255.255.255.127) being reported for any address
if last octet > 127 when sizeof(long) == 4.
2017-01-05 11:20:07 +03:00
Samrat Guha Niyogi
6e34792323 sim: remove locked_pin reset from sim_free_main_state 2017-01-05 11:20:07 +03:00
Samrat Guha Niyogi
d6a59f5dc4 rilmodem: fix cbd init in ril_query_facility_lock 2017-01-05 11:20:07 +03:00
Samrat Guha Niyogi
23e299055f rilmodem: fix num_params in query_facility_lock_cb 2017-01-05 11:20:07 +03:00
Denis Kenzior
a56ef3ba0f rilmodem: Co-locate the callback with invocation 2017-01-05 11:20:07 +03:00
Denis Kenzior
7294433906 rilmodem: Co-locate the callback with invocation 2017-01-05 11:20:07 +03:00
Samrat Guha Niyogi
d7263cd344 rilmodem: split ril_pin_change_state_cb
split ril_pin_change_state_cb based on functionality w.r.t.
facility lock, puk and pin. Rename ril_pin_change_state to
more appropriate name ril_set_facility_lock and rename
ril_pin_change_state_cb to ril_enter_sim_pin_cb.
2017-01-05 11:20:07 +03:00
Samrat Guha Niyogi
2f3b469fbb rilmodem: Remove unneeded pin sending logic 2017-01-05 11:20:07 +03:00
Denis Kenzior
4187e7ee8f sim: Fix style 2017-01-05 11:20:07 +03:00
Samrat Guha Niyogi
4d3f89bae0 sim: query facility during initialization in chain manner
query facility during initialization is modified from back
to back invocation to chain manner to keep it inline with
RIL design. All vendor RIL does not support back to back
handling since RIL telephony framework sends the request
synchronously.
2017-01-05 11:20:07 +03:00
Denis Kenzior
cbd1c5d524 sim: return false for SIM_PASSWORD_NONE 2017-01-05 11:20:07 +03:00
Ankit Navik
7976e44746 ril_sofia3gr: use vendor modem power down command 2017-01-05 11:20:07 +03:00
Denis Kenzior
3d6e220686 test: Add cancel-sms script 2017-01-05 11:20:07 +03:00
Denis Kenzior
919526d392 test: Fix permissions 2017-01-05 11:20:07 +03:00
Anirudh Gargi
b7082146e8 gril: Fix print macros to use latest print_buf 2017-01-05 11:20:07 +03:00
Denis Kenzior
094a296a14 AUTHORS: Mention Rajagopal's contributions 2017-01-05 11:20:07 +03:00
Rajagopal Aravindan
5c259e751b test: Added test to disable sofia 3gr tx throttling 2017-01-05 11:20:07 +03:00
Rajagopal Aravindan
69e5d5b356 test: Added test to enable sofia 3gr tx throttling 2017-01-05 11:20:07 +03:00
Rajagopal Aravindan
56e7d0e8ea ril_sofia3gr: Add transmit power throttling
Added 'sofia3gr.ThermalManagement' interface to sofia3gr plugin and
floated 'TransmitPowerThrottling' as an ofono property under that,
to support modem throttling.

'sofia3gr.ThermalManagement'
    1.Will be available ONLY if modem throttling is supported in RIL.
      This is determined by sending a RIL request during ofono start,
      to get current throttling state. If it succeeds, interface is
      published otherwise not.

'TransmitPowerThrottling'
    1.GetProperties will be allowed both when modem is ON & OFF.
    2.GetProperties will be serviced by looking it up from state
      information maintained inside ofono. No RIL call will be made.
    3.SetProperty will be allowed ONLY when modem is ON.
    4.SetProperty request will be forwarded to RIL ONLY if
      new state != current state. If RIL request succeeds, state
      information will be updated and also, a property change signal
      will be emitted.
2017-01-05 11:20:07 +03:00
Nishanth V
bb2ae6d1a1 rilmodem: fix WCDMA only in query rat mode 2017-01-05 11:20:07 +03:00
Caiwen Zhang
cf7692db49 plugins/ril: enable cbs 2017-01-05 11:20:07 +03:00
Caiwen Zhang
63f3311cd6 rilmodem: add cell broadcast message support 2017-01-05 11:20:07 +03:00
Samrat Guha Niyogi
c5aae77d41 ril_sofia3gr: Return -EINPROGRESS on disable() 2017-01-05 11:20:07 +03:00
Denis Kenzior
f8e21c8ad4 AUTHORS: Mention Suman's contributions 2017-01-05 11:20:07 +03:00
Suman Mallela
8e6dfe433b rilmodem: Fix PIN retries logic
The SIM PIN retries were not getting reset when the correct PIN/PUK
was entered.
2017-01-05 11:20:07 +03:00
Caiwen Zhang
ae23bb552b gril: avoid glib runtime warning
gril may be destroyed in the request callback (e.g in the callback of
set modem power off request). 'out_queue' and 'command_queue' is NULL.
there will be glib runtime warning if use them.
2017-01-05 11:20:06 +03:00
Caiwen Zhang
2becd051d4 plugins/ril: complete modem power off process 2017-01-05 11:20:06 +03:00
Slava Monich
8cfb1d5ca3 Merge branch 'cbs' into 'master'
Enable Cell Broadcast support

It was totally broken. 

See merge request !100
2017-01-05 08:15:45 +00:00
Slava Monich
50f35458f6 Merge branch 'rat' into 'master'
Preferred network mode, more corner cases

See merge request !99
2017-01-05 08:14:43 +00:00
Slava Monich
51843accf7 [ril] Enable Cell Broadcast support. Fixes MER#1729 2017-01-04 20:34:54 +03:00
Slava Monich
fb856dc7d6 [ril] Preferred network mode, more corner cases. Contributes to JB#36683
Prevents repeated rat mode setting failures when data SIM is not selected.
2017-01-04 16:05:11 +03:00
104 changed files with 14811 additions and 4657 deletions

14
ofono/.gitignore vendored
View File

@@ -44,10 +44,24 @@ unit/test-stkutil
unit/test-cdmasms
unit/test-rilmodem-cb
unit/test-rilmodem-cs
unit/test-rilmodem-gprs
unit/test-rilmodem-sms
unit/test-*.log
unit/test-*.trs
unit/test-grilreply
unit/test-grilrequest
unit/test-grilunsol
unit/test-provision
unit/html
drivers/*/*.gcda
drivers/*/*.gcno
drivers/*/*.gcov
*/*.gcda
*/*.gcno
*/*.gcov
tools/huawei-audio
tools/auto-enable
tools/get-location

View File

@@ -113,3 +113,6 @@ Anirudh Gargi <anirudh.gargi@intel.com>
Nishanth V <nishanth.v@intel.com>
Antara Borwankar <antara.borwankar@gmail.com>
Martin Chaplet <m.chaplet@kerlink.fr>
Suman Mallela <suman.m@intel.com>
Rajagopal Aravindan <rajagopalx.aravindan@intel.com>
Antoine Aubert <a.aubert@overkiz.com>

View File

@@ -1,3 +1,13 @@
ver 1.19:
Fix issue with DHCP parsing and Huawei modems.
Fix issue with detecting Huawei E3372 modem.
Fix issue with handling serving cell info.
Fix issue with handling SIM SC facility lock.
Fix issue with Android RIL PIN retry logic.
Fix issue with Android RIL and RAT handling.
Add support for Android RIL cell broadcast.
Add support for SoFIA 3GR thermal management.
ver 1.18:
Fix issue with cell broadcast and use-after-fee.
Fix issue with repeated held call indicator.

View File

@@ -27,6 +27,11 @@ 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_manager.h \
include/sailfish_watch.h
endif
local_headers = $(foreach file,$(pkginclude_HEADERS) \
$(nodist_pkginclude_HEADERS), \
include/ofono/$(notdir $(file)))
@@ -116,8 +121,17 @@ builtin_modules += udevng
builtin_sources += plugins/udevng.c
endif
if SAILFISH_MANAGER
builtin_modules += sailfish_manager
builtin_sources += plugins/sailfish_manager/sailfish_manager.c \
plugins/sailfish_manager/sailfish_manager_dbus.c \
plugins/sailfish_manager/sailfish_sim_info.c \
plugins/sailfish_manager/sailfish_sim_info_dbus.c \
plugins/sailfish_manager/sailfish_watch.c
endif
if RILMODEM
if JOLLA_RILMODEM
if SAILFISH_RILMODEM
builtin_modules += ril
builtin_sources += drivers/ril/ril_call_barring.c \
@@ -134,20 +148,17 @@ builtin_sources += drivers/ril/ril_call_barring.c \
drivers/ril/ril_gprs.c \
drivers/ril/ril_gprs_context.c \
drivers/ril/ril_modem.c \
drivers/ril/ril_mtu.c \
drivers/ril/ril_netmon.c \
drivers/ril/ril_netreg.c \
drivers/ril/ril_network.c \
drivers/ril/ril_oem_raw.c \
drivers/ril/ril_phonebook.c \
drivers/ril/ril_plugin.c \
drivers/ril/ril_plugin_dbus.c \
drivers/ril/ril_radio.c \
drivers/ril/ril_radio_caps.c \
drivers/ril/ril_radio_settings.c \
drivers/ril/ril_sim.c \
drivers/ril/ril_sim_card.c \
drivers/ril/ril_sim_info.c \
drivers/ril/ril_sim_info_dbus.c \
drivers/ril/ril_sim_settings.c \
drivers/ril/ril_sms.c \
drivers/ril/ril_stk.c \
@@ -197,6 +208,7 @@ builtin_sources += drivers/rilmodem/rilmodem.h \
drivers/rilmodem/call-barring.c \
drivers/rilmodem/netmon.c \
drivers/rilmodem/stk.c \
drivers/rilmodem/cbs.c \
drivers/infineonmodem/infineon_constants.h
endif
endif
@@ -574,6 +586,11 @@ builtin_sources += plugins/bluez5.c plugins/bluez5.h
builtin_modules += hfp_ag_bluez5
builtin_sources += plugins/hfp_ag_bluez5.c plugins/bluez5.h
if SAILFISH_BT
builtin_modules += sfos_bt
builtin_sources += plugins/sailfish_bt.c
endif
endif
if UPOWER
@@ -587,11 +604,25 @@ builtin_modules += nettime
builtin_sources += plugins/nettime.c
endif
if SAILFISH_DEBUGLOG
builtin_modules += debuglog
builtin_sources += plugins/sailfish_debuglog.c
endif
if SAILFISH_PROVISION
builtin_sources += plugins/sailfish_provision.c
PROVISION = 1
else
if PROVISION
builtin_sources += plugins/provision.c
endif
endif
if PROVISION
builtin_sources += plugins/mbpi.h plugins/mbpi.c
builtin_modules += provision
builtin_sources += plugins/provision.h plugins/provision.c
builtin_sources += plugins/provision.h
builtin_modules += cdma_provision
builtin_sources += plugins/cdma-provision.c
@@ -629,16 +660,9 @@ builtin_sources += plugins/smart-messaging.c
builtin_modules += push_notification
builtin_sources += plugins/push-notification.c
if PUSHFORWARDER
builtin_modules += push_forwarder
builtin_sources += plugins/push-forwarder.c
builtin_cflags += @WSPCODEC_CFLAGS@
builtin_libadd += @WSPCODEC_LIBS@
endif
if DEBUGLOG
builtin_modules += debuglog
builtin_sources += plugins/debuglog.c
if SAILFISH_PUSHFORWARDER
builtin_modules += pushforwarder
builtin_sources += plugins/sailfish_pushforwarder.c
endif
builtin_modules += sms_history
@@ -650,6 +674,7 @@ builtin_sources += plugins/allowed-apns.c
sbin_PROGRAMS = src/ofonod
src_ofonod_SOURCES = $(builtin_sources) $(gatchat_sources) src/ofono.ver \
src/mtu-watch.c \
src/main.c src/ofono.h src/log.c src/plugin.c \
src/modem.c src/common.h src/common.c \
src/manager.c src/dbus.c src/util.h src/util.c \
@@ -758,6 +783,7 @@ test_scripts = test/backtrace \
test/receive-sms \
test/remove-contexts \
test/send-sms \
test/cancel-sms \
test/set-mic-volume \
test/set-speaker-volume \
test/test-stk-menu \
@@ -828,7 +854,9 @@ test_scripts = test/backtrace \
test/set-sms-smsc \
test/set-sms-bearer \
test/get-serving-cell-info \
test/list-allowed-access-points
test/list-allowed-access-points \
test/enable-throttling \
test/disable-throttling
if TEST
testdir = $(pkglibdir)/test
@@ -840,6 +868,9 @@ EXTRA_DIST = src/genbuiltin plugins/ofono.rules plugins/ofono-speedup.rules \
dist_man_MANS = doc/ofonod.8
if TEST_COVERAGE
COVERAGE_OPT = --coverage
endif
unit_objects =
@@ -848,13 +879,39 @@ unit_tests = unit/test-common unit/test-util unit/test-idmap \
unit/test-sms unit/test-cdmasms \
unit/test-provision
if SAILFISH_MANAGER
unit_test_sailfish_sim_info_SOURCES = unit/test-sailfish_sim_info.c \
unit/fake_sailfish_watch.c \
plugins/sailfish_manager/sailfish_sim_info.c \
src/storage.c src/watch.c src/log.c
unit_test_sailfish_sim_info_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS) \
-DSTORAGEDIR='"/tmp/ofono"' -Iplugins/sailfish_manager
unit_test_sailfish_sim_info_LDADD = @GLIB_LIBS@ -ldl
unit_objects += $(unit_test_sailfish_sim_info_OBJECTS)
unit_tests += unit/test-sailfish_sim_info
unit_test_sailfish_manager_SOURCES = unit/test-sailfish_manager.c \
unit/fake_sailfish_watch.c \
plugins/sailfish_manager/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
unit_test_sailfish_manager_LDADD = @GLIB_LIBS@ -ldl
unit_objects += $(unit_test_sailfish_manager_OBJECTS)
unit_tests += unit/test-sailfish_manager
endif
if RILMODEM
if JOLLA_RILMODEM
if SAILFISH_RILMODEM
unit_tests += unit/test-rilmodem-cs \
unit/test-rilmodem-cs \
unit/test-rilmodem-sms \
unit/test-rilmodem-cb
unit/test-rilmodem-cb \
unit/test-rilmodem-gprs
endif
endif
@@ -863,19 +920,23 @@ noinst_PROGRAMS = $(unit_tests) \
unit/test-sms-root unit/test-mux unit/test-caif
unit_test_common_SOURCES = unit/test-common.c src/common.c src/util.c
unit_test_common_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
unit_test_common_LDADD = @GLIB_LIBS@
unit_objects += $(unit_test_common_OBJECTS)
unit_test_util_SOURCES = unit/test-util.c src/util.c
unit_test_util_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
unit_test_util_LDADD = @GLIB_LIBS@
unit_objects += $(unit_test_utils_OBJECTS)
unit_test_idmap_SOURCES = unit/test-idmap.c src/idmap.c
unit_test_idmap_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
unit_test_idmap_LDADD = @GLIB_LIBS@
unit_objects += $(unit_test_idmap_OBJECTS)
unit_test_simutil_SOURCES = unit/test-simutil.c src/util.c \
src/simutil.c src/smsutil.c src/storage.c
unit_test_simutil_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
unit_test_simutil_LDADD = @GLIB_LIBS@
unit_objects += $(unit_test_simutil_OBJECTS)
@@ -883,19 +944,23 @@ unit_test_stkutil_SOURCES = unit/test-stkutil.c unit/stk-test-data.h \
src/util.c \
src/storage.c src/smsutil.c \
src/simutil.c src/stkutil.c
unit_test_stkutil_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
unit_test_stkutil_LDADD = @GLIB_LIBS@
unit_objects += $(unit_test_stkutil_OBJECTS)
unit_test_sms_SOURCES = unit/test-sms.c src/util.c src/smsutil.c src/storage.c
unit_test_sms_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
unit_test_sms_LDADD = @GLIB_LIBS@
unit_objects += $(unit_test_sms_OBJECTS)
unit_test_cdmasms_SOURCES = unit/test-cdmasms.c src/cdma-smsutil.c
unit_test_cdmasms_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
unit_test_cdmasms_LDADD = @GLIB_LIBS@
unit_objects += $(unit_test_cdmasms_OBJECTS)
unit_test_sms_root_SOURCES = unit/test-sms-root.c \
src/util.c src/smsutil.c src/storage.c
unit_test_sms_root_CFLAGS = -DSTORAGEDIR='"/tmp/ofono"' $(COVERAGE_OPT) $(AM_CFLAGS)
unit_test_sms_root_LDADD = @GLIB_LIBS@
unit_objects += $(unit_test_sms_root_OBJECTS)
@@ -906,24 +971,26 @@ unit_objects += $(unit_test_mux_OBJECTS)
unit_test_caif_SOURCES = unit/test-caif.c $(gatchat_sources) \
drivers/stemodem/caif_socket.h \
drivers/stemodem/if_caif.h
unit_test_caif_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
unit_test_caif_LDADD = @GLIB_LIBS@
unit_objects += $(unit_test_caif_OBJECTS)
unit_test_provision_SOURCES = unit/test-provision.c \
plugins/provision.h plugins/provision.c \
plugins/mbpi.c src/gprs-provision.c \
src/log.c
plugins/provision.h plugins/mbpi.c \
plugins/sailfish_provision.c \
src/gprs-provision.c src/log.c
unit_test_provision_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
unit_test_provision_LDADD = @GLIB_LIBS@ -ldl
unit_objects += $(unit_test_provision_OBJECTS)
if RILMODEM
if JOLLA_RILMODEM
test_rilmodem_sources = $(gril_sources) src/log.c src/common.c src/util.c \
gatchat/ringbuffer.h gatchat/ringbuffer.c \
unit/rilmodem-test-server.h \
unit/rilmodem-test-server.c \
src/simutil.c
unit/rilmodem-test-engine.h \
unit/rilmodem-test-engine.c \
src/simutil.c \
drivers/rilmodem/rilutil.c
unit_test_rilmodem_cs_SOURCES = $(test_rilmodem_sources) \
unit/test-rilmodem-cs.c \
@@ -946,8 +1013,12 @@ unit_test_rilmodem_cb_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \
@GLIB_LIBS@ @DBUS_LIBS@ -ldl
unit_objects += $(unit_test_rilmodem_cb_OBJECTS)
endif
endif
unit_test_rilmodem_gprs_SOURCES = $(test_rilmodem_sources) \
unit/test-rilmodem-gprs.c \
drivers/rilmodem/gprs.c
unit_test_rilmodem_gprs_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \
@GLIB_LIBS@ @DBUS_LIBS@ -ldl
unit_objects += $(unit_test_rilmodem_gprs_OBJECTS)
TESTS = $(unit_tests)

View File

@@ -1,5 +1,5 @@
AC_PREREQ(2.60)
AC_INIT(ofono, 1.18)
AC_INIT(ofono, 1.19)
AM_INIT_AUTOMAKE([foreign subdir-objects color-tests])
AC_CONFIG_HEADERS(config.h)
@@ -69,6 +69,16 @@ PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.32, dummy=yes,
AC_SUBST(GLIB_CFLAGS)
AC_SUBST(GLIB_LIBS)
PKG_CHECK_MODULES(GOBJECT, gobject-2.0, dummy=yes,
AC_MSG_ERROR(GObject is required))
GLIB_CFLAGS="$GLIB_CFLAGS $GOBJECT_CFLAGS"
GLIB_LIBS="$GLIB_LIBS $GOBJECT_LIBS"
PKG_CHECK_MODULES(GIO, gio-2.0, dummy=yes,
AC_MSG_ERROR(GIO is required))
GLIB_CFLAGS="$GLIB_CFLAGS $GIO_CFLAGS"
GLIB_LIBS="$GLIB_LIBS $GIO_LIBS"
if (test "${enable_threads}" = "yes"); then
AC_DEFINE(NEED_THREADS, 1, [Define if threading support is required])
PKG_CHECK_MODULES(GTHREAD, gthread-2.0 >= 2.16, dummy=yes,
@@ -167,22 +177,44 @@ AC_ARG_ENABLE(rilmodem, AC_HELP_STRING([--disable-rilmodem],
[enable_rilmodem=${enableval}])
AM_CONDITIONAL(RILMODEM, test "${enable_rilmodem}" != "no")
AC_ARG_ENABLE(jolla-rilmodem,
AC_HELP_STRING([--enable-jolla-rilmodem], [enable Jolla RIL modem]),
[enable_jolla_rilmodem=${enableval}], [enable_jolla_rilmodem="no"])
AM_CONDITIONAL(JOLLA_RILMODEM, test "${enable_jolla_rilmodem}" != "no")
AC_ARG_ENABLE(sailfish-rilmodem, AC_HELP_STRING([--enable-sailfish-rilmodem],
[enable Sailfish RIL modem]),
[enable_sailfish_rilmodem=${enableval}],
[enable_sailfish_rilmodem="no"])
AM_CONDITIONAL(SAILFISH_RILMODEM, test "${enable_sailfish_rilmodem}" != "no")
if (test "${enable_jolla_rilmodem}" = "yes"); then
PKG_CHECK_MODULES(GRILIO, libgrilio >= 1.0.6, dummy=yes,
AC_MSG_ERROR(libgrilio >= 1.0.6 is required))
PKG_CHECK_MODULES(GLIBUTIL, libglibutil >= 1.0.5, dummy=yes,
AC_MSG_ERROR(libglibutil >= 1.0.5 is required))
if (test "${enable_sailfish_rilmodem}" = "yes"); then
PKG_CHECK_MODULES(GRILIO, libgrilio >= 1.0.16, dummy=yes,
AC_MSG_ERROR(libgrilio >= 1.0.16 is required))
PKG_CHECK_MODULES(GLIBUTIL, libglibutil >= 1.0.23, dummy=yes,
AC_MSG_ERROR(libglibutil >= 1.0.23 is required))
PKG_CHECK_MODULES(LIBMCE, libmce-glib, dummy=yes,
AC_MSG_ERROR(libmce-glib is required))
CFLAGS="$CFLAGS $GRILIO_CFLAGS $GLIBUTIL_CFLAGS $LIBMCE_CFLAGS"
LIBS="$LIBS $GRILIO_LIBS $GLIBUTIL_LIBS $LIBMCE_LIBS"
CFLAGS="$CFLAGS $GRILIO_CFLAGS $LIBMCE_CFLAGS"
LIBS="$LIBS $GRILIO_LIBS $LIBMCE_LIBS"
enable_sailfish_manager=yes
need_glibutil=yes
fi
AC_ARG_ENABLE(sailfish-manager,
AC_HELP_STRING([--enable-sailfish-manager],
[enable Sailfish OS modem manager plugin]),
[enable_sailfish_manager=${enableval}])
AM_CONDITIONAL(SAILFISH_MANAGER, test "${enable_sailfish_manager}" = "yes")
AC_ARG_ENABLE(add-remove-context, AC_HELP_STRING([--disable-add-remove-context],
[don't allow to add or remove connection context over D-Bus]), [
if (test "${enableval}" = "no"); then
CFLAGS="$CFLAGS -DDISABLE_ADD_REMOVE_CONTEXT"
fi
])
AC_ARG_ENABLE(test-coverage,
AC_HELP_STRING([--enable-test-coverage], [enable test code coverage]),
[enable_test_coverage=${enableval}],
[enable_test_coverage="no"])
AM_CONDITIONAL(TEST_COVERAGE, test "${enable_test_coverage}" != "no")
AC_ARG_ENABLE(qmimodem, AC_HELP_STRING([--disable-qmimodem],
[disable Qualcomm QMI modem support]),
[enable_qmimodem=${enableval}])
@@ -206,6 +238,16 @@ fi
AM_CONDITIONAL(BLUEZ4, test "${enable_bluetooth}" != "no" && test "${enable_bluez4}" = "yes")
AM_CONDITIONAL(BLUETOOTH, test "${enable_bluetooth}" != "no")
AC_ARG_ENABLE(sailfish-bt, AC_HELP_STRING([--enable-sailfish-bt],
[enable Sailfish OS Bluetooth plugin]),
[enable_sailfish_bt=${enableval}])
AM_CONDITIONAL(SAILFISH_BT, test "${enable_sailfish_bt}" = "yes")
AC_ARG_ENABLE(sailfish-provision, AC_HELP_STRING([--enable-sailfish-provision],
[enable Sailfish OS provisioning plugin]),
[enable_sailfish_provision=${enableval}])
AM_CONDITIONAL(SAILFISH_PROVISION, test "${enable_sailfish_provision=$}" = "yes")
AC_ARG_ENABLE(nettime, AC_HELP_STRING([--disable-nettime],
[disable Nettime plugin]),
[enable_nettime=${enableval}])
@@ -248,28 +290,38 @@ AC_ARG_ENABLE(datafiles, AC_HELP_STRING([--disable-datafiles],
[enable_datafiles=${enableval}])
AM_CONDITIONAL(DATAFILES, test "${enable_datafiles}" != "no")
AC_ARG_ENABLE(pushforwarder, AC_HELP_STRING([--disable-pushforwarder],
[disable Push Forwarder plugin]),
[enable_pushforwarder=${enableval}])
AM_CONDITIONAL(PUSHFORWARDER, test "${enable_pushforwarder}" != "no")
if (test "${enable_pushforwarder}" != "no"); then
AC_ARG_ENABLE(sailfish-pushforwarder, AC_HELP_STRING([--enable-sailfish-pushforwarder],
[enable Sailfish OS push forwarder plugin]),
[enable_sailfish_pushforwarder=${enableval}],
[enable_sailfish_pushforwarder="no"])
AM_CONDITIONAL(SAILFISH_PUSHFORWARDER, test "${enable_sailfish_pushforwarder}" != "no")
if (test "${enable_sailfish_pushforwarder}" != "no"); then
PKG_CHECK_MODULES(GLIBUTIL, libglibutil >= 1.0.15, dummy=yes,
AC_MSG_ERROR(libglibutil >= 1.0.15 is required))
PKG_CHECK_MODULES(WSPCODEC, libwspcodec >= 2.0, dummy=yes,
AC_MSG_ERROR(WSP decoder is required))
AC_SUBST(WSPCODEC_CFLAGS)
AC_SUBST(WSPCODEC_LIBS)
AC_MSG_ERROR(WSP decoder is required))
CFLAGS="$CFLAGS $WSPCODEC_CFLAGS"
LIBS="$LIBS $WSPCODEC_LIBS"
need_glibutil=yes
fi
AC_ARG_ENABLE(debuglog,
AC_HELP_STRING([--enable-debuglog], [enable log control plugin]),
[enable_debuglog=${enableval}], [enable_debuglog="no"])
AM_CONDITIONAL(DEBUGLOG, test "${enable_debuglog}" != "no")
if (test "${enable_debuglog}" = "yes"); then
AC_ARG_ENABLE(sailfish-debuglog, AC_HELP_STRING([--enable-sailfish-debuglog],
[enable Sailfish OS debug log plugin]),
[enable_sailfish_debuglog=${enableval}],
[enable_sailfish_debuglog="no"])
AM_CONDITIONAL(SAILFISH_DEBUGLOG, test "${enable_sailfish_debuglog}" != "no")
if (test "${enable_sailfish_debuglog}" = "yes"); then
PKG_CHECK_MODULES(DBUSLOG, libdbuslogserver-dbus, dummy=yes,
AC_MSG_ERROR(libdbuslogserver-dbus is required))
CFLAGS="$CFLAGS $DBUSLOG_CFLAGS"
LIBS="$LIBS $DBUSLOG_LIBS"
fi
if (test "${need_glibutil}" = "yes"); then
CFLAGS="$CFLAGS $GLIBUTIL_CFLAGS"
LIBS="$LIBS $GLIBUTIL_LIBS"
fi
if (test "${prefix}" = "NONE"); then
dnl no prefix and no localstatedir, so default to /var
if (test "$localstatedir" = '${prefix}/var'); then
@@ -284,7 +336,7 @@ if (test "$localstatedir" = '${prefix}/var'); then
else
storagedir="${localstatedir}/lib/ofono"
fi
AC_DEFINE_UNQUOTED(STORAGEDIR, "${storagedir}",
AC_DEFINE_UNQUOTED(DEFAULT_STORAGEDIR, "${storagedir}",
[Directory for the storage files])
if (test "$sysconfdir" = '${prefix}/etc'); then

View File

@@ -247,6 +247,8 @@ static void at_gprs_activate_primary(struct ofono_gprs_context *gc,
/* We only support CHAP and PAP */
switch (ctx->auth_method) {
case OFONO_GPRS_AUTH_METHOD_ANY:
case OFONO_GPRS_AUTH_METHOD_NONE:
case OFONO_GPRS_AUTH_METHOD_CHAP:
gcd->auth_method = G_AT_PPP_AUTH_METHOD_CHAP;
break;
@@ -294,6 +296,8 @@ static void at_gprs_activate_primary(struct ofono_gprs_context *gc,
* prefix, this is the least invasive place to set it.
*/
switch (ctx->auth_method) {
case OFONO_GPRS_AUTH_METHOD_ANY:
case OFONO_GPRS_AUTH_METHOD_NONE:
case OFONO_GPRS_AUTH_METHOD_CHAP:
snprintf(buf + len, sizeof(buf) - len - 3,
",\"CHAP:%s\"", ctx->apn);

View File

@@ -80,7 +80,7 @@ static gboolean get_next_addr(GAtResultIter *iter, char **addr)
if (g_at_result_iter_next_unquoted_string(iter, &str) == FALSE)
return FALSE;
val = strtol(str, NULL, 16);
val = strtoul(str, NULL, 16);
if (addr)
*addr = g_strdup_printf("%u.%u.%u.%u",

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -16,7 +16,6 @@
#include "ril_plugin.h"
#include "ril_util.h"
#include "ril_log.h"
#include "ril_constants.h"
#include "common.h"

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2016 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -16,7 +16,6 @@
#include "ril_plugin.h"
#include "ril_util.h"
#include "ril_log.h"
#include "ril_constants.h"
#include "common.h"

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -16,7 +16,6 @@
#include "ril_plugin.h"
#include "ril_util.h"
#include "ril_log.h"
#include "ril_constants.h"
#include "common.h"

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -16,7 +16,6 @@
#include "ril_plugin.h"
#include "ril_util.h"
#include "ril_log.h"
#include "ril_constants.h"
struct ril_call_volume {
struct ofono_call_volume *v;

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -16,60 +16,155 @@
#include "ril_plugin.h"
#include "ril_util.h"
#include "ril_log.h"
#include "ril_constants.h"
#include <gutil_strv.h>
struct ril_cbs {
struct ofono_cbs *cbs;
GRilIoChannel *io;
guint timer_id;
GRilIoQueue *q;
char *log_prefix;
gulong event_id;
};
static void ril_set_topics(struct ofono_cbs *cbs, const char *topics,
ofono_cbs_set_cb_t cb, void *data)
struct ril_cbs_cbd {
struct ril_cbs *cd;
ofono_cbs_set_cb_t cb;
gpointer data;
};
#define RIL_CBS_CHECK_RETRY_MS 1000
#define RIL_CBS_CHECK_RETRY_COUNT 30
#define DBG_(cd,fmt,args...) DBG("%s" fmt, (cd)->log_prefix, ##args)
#define ril_cbs_cbd_free g_free
static struct ril_cbs_cbd *ril_cbs_cbd_new(struct ril_cbs *cd,
ofono_cbs_set_cb_t cb, void *data)
{
struct ofono_error error;
cb(ril_error_ok(&error), data);
struct ril_cbs_cbd *cbd = g_new(struct ril_cbs_cbd, 1);
cbd->cd = cd;
cbd->cb = cb;
cbd->data = data;
return cbd;
}
static void ril_clear_topics(struct ofono_cbs *cbs,
static void ril_cbs_request_activation(struct ril_cbs *cd,
gboolean activate, GRilIoChannelResponseFunc response,
GDestroyNotify destroy, void* user_data)
{
GRilIoRequest* req = grilio_request_sized_new(8);
grilio_request_append_int32(req, 1);
grilio_request_append_int32(req, activate ? 0 :1);
DBG_(cd, "%sactivating CB", activate ? "" : "de");
grilio_queue_send_request_full(cd->q, req,
RIL_REQUEST_GSM_SMS_BROADCAST_ACTIVATION,
response, destroy, user_data);
grilio_request_unref(req);
}
static void ril_cbs_set_config(struct ril_cbs *cd, const char *topics,
GRilIoChannelResponseFunc response,
GDestroyNotify destroy, void* user_data)
{
char **list = topics ? g_strsplit(topics, ",", 0) : NULL;
int i, n = gutil_strv_length(list);
GRilIoRequest* req = grilio_request_new();
grilio_request_append_int32(req, n);
for (i = 0; i < n; i++) {
const char *entry = list[i];
const char *delim = strchr(entry, '-');
int from, to;
if (delim) {
char **range = g_strsplit(topics, "-", 0);
from = atoi(range[0]);
to = atoi(range[1]);
g_strfreev(range);
} else {
from = to = atoi(entry);
}
grilio_request_append_int32(req, from);
grilio_request_append_int32(req, to);
grilio_request_append_int32(req, 0);
grilio_request_append_int32(req, 0xff);
grilio_request_append_int32(req, 1);
}
DBG_(cd, "configuring CB");
grilio_queue_send_request_full(cd->q, req,
RIL_REQUEST_GSM_SET_BROADCAST_SMS_CONFIG,
response, destroy, user_data);
grilio_request_unref(req);
g_strfreev(list);
}
static void ril_cbs_cb(GRilIoChannel *io, int ril_status,
const void *data, guint len, void *user_data)
{
struct ril_cbs_cbd *cbd = user_data;
if (cbd->cb) {
struct ofono_error error;
if (ril_status == RIL_E_SUCCESS) {
cbd->cb(ril_error_ok(&error), cbd->data);
} else {
cbd->cb(ril_error_failure(&error), cbd->data);
}
}
}
static void ril_cbs_set_topics(struct ofono_cbs *cbs, const char *topics,
ofono_cbs_set_cb_t cb, void *data)
{
struct ofono_error error;
cb(ril_error_ok(&error), data);
struct ril_cbs *cd = ofono_cbs_get_data(cbs);
DBG_(cd, "%s", topics);
ril_cbs_set_config(cd, topics, ril_cbs_cb, ril_cbs_cbd_free,
ril_cbs_cbd_new(cd, cb, data));
}
static void ril_cbs_clear_topics(struct ofono_cbs *cbs,
ofono_cbs_set_cb_t cb, void *data)
{
struct ril_cbs *cd = ofono_cbs_get_data(cbs);
DBG_(cd, "");
ril_cbs_request_activation(cd, FALSE, ril_cbs_cb, ril_cbs_cbd_free,
ril_cbs_cbd_new(cd, cb, data));
}
static void ril_cbs_notify(GRilIoChannel *io, guint code,
const void *data, guint len, void *user_data)
{
struct ril_cbs *cd = user_data;
GRilIoParser rilp;
char* pdu;
GASSERT(code == RIL_UNSOL_ON_USSD);
grilio_parser_init(&rilp, data, len);
pdu = grilio_parser_get_utf8(&rilp);
DBG("%s", pdu);
if (pdu) {
ofono_cbs_notify(cd->cbs, (const guchar *)pdu, strlen(pdu));
g_free(pdu);
}
GASSERT(code == RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS);
DBG_(cd, "%u bytes", len);
ofono_cbs_notify(cd->cbs, data, len);
}
static gboolean ril_cbs_register(gpointer user_data)
static void ril_cbs_probe_done_cb(GRilIoChannel *io, int status,
const void *data, guint len, void *user_data)
{
struct ril_cbs *cd = user_data;
DBG("");
GASSERT(cd->timer_id);
cd->timer_id = 0;
ofono_cbs_register(cd->cbs);
cd->event_id = grilio_channel_add_unsol_event_handler(cd->io,
ril_cbs_notify, RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS, cd);
/* Single-shot */
return FALSE;
if (status == RIL_E_SUCCESS) {
DBG_(cd, "registering for CB");
cd->event_id = grilio_channel_add_unsol_event_handler(cd->io,
ril_cbs_notify, RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS,
cd);
ofono_cbs_register(cd->cbs);
} else {
DBG_(cd, "failed to query CB config");
ofono_cbs_remove(cd->cbs);
}
}
static int ril_cbs_probe(struct ofono_cbs *cbs, unsigned int vendor,
@@ -77,12 +172,30 @@ static int ril_cbs_probe(struct ofono_cbs *cbs, unsigned int vendor,
{
struct ril_modem *modem = data;
struct ril_cbs *cd = g_try_new0(struct ril_cbs, 1);
GRilIoRequest* req = grilio_request_new();
DBG("");
cd->cbs = cbs;
cd->io = grilio_channel_ref(ril_modem_io(modem));
cd->timer_id = g_idle_add(ril_cbs_register, cd);
ofono_cbs_set_data(cbs, cd);
cd->log_prefix = (modem->log_prefix && modem->log_prefix[0]) ?
g_strconcat(modem->log_prefix, " ", NULL) : g_strdup("");
cd->cbs = cbs;
DBG_(cd, "");
cd->io = grilio_channel_ref(ril_modem_io(modem));
cd->q = grilio_queue_new(cd->io);
/*
* RIL_REQUEST_GSM_GET_BROADCAST_SMS_CONFIG often fails at startup
* especially if other RIL requests are running in parallel. We may
* have to retry a few times. Also, make it blocking in order to
* improve the chance of success.
*/
grilio_request_set_retry(req, RIL_CBS_CHECK_RETRY_MS,
RIL_CBS_CHECK_RETRY_COUNT);
grilio_request_set_blocking(req, TRUE);
grilio_queue_send_request_full(cd->q, req,
RIL_REQUEST_GSM_GET_BROADCAST_SMS_CONFIG,
ril_cbs_probe_done_cb, NULL, cd);
grilio_request_unref(req);
return 0;
}
@@ -90,15 +203,13 @@ static void ril_cbs_remove(struct ofono_cbs *cbs)
{
struct ril_cbs *cd = ofono_cbs_get_data(cbs);
DBG("");
DBG_(cd, "");
ofono_cbs_set_data(cbs, NULL);
if (cd->timer_id > 0) {
g_source_remove(cd->timer_id);
}
grilio_channel_remove_handler(cd->io, cd->event_id);
grilio_channel_unref(cd->io);
grilio_queue_cancel_all(cd->q, FALSE);
grilio_queue_unref(cd->q);
g_free(cd->log_prefix);
g_free(cd);
}
@@ -106,8 +217,8 @@ const struct ofono_cbs_driver ril_cbs_driver = {
.name = RILMODEM_DRIVER,
.probe = ril_cbs_probe,
.remove = ril_cbs_remove,
.set_topics = ril_set_topics,
.clear_topics = ril_clear_topics
.set_topics = ril_cbs_set_topics,
.clear_topics = ril_cbs_clear_topics
};
/*

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2016 Jolla Ltd.
* Copyright (C) 2016-2017 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
@@ -28,7 +28,6 @@ struct ril_cell_entry {
};
struct ril_cell_info_dbus {
struct ril_modem *md;
struct ril_cell_info *info;
DBusConnection *conn;
char *path;
@@ -523,7 +522,6 @@ struct ril_cell_info_dbus *ril_cell_info_dbus_new(struct ril_modem *md,
struct ril_cell_info_dbus *dbus = g_new0(struct ril_cell_info_dbus, 1);
DBG("%s", ril_modem_get_path(md));
dbus->md = md;
dbus->path = g_strdup(ril_modem_get_path(md));
dbus->conn = dbus_connection_ref(ofono_dbus_get_connection());
dbus->info = ril_cell_info_ref(info);
@@ -553,8 +551,6 @@ void ril_cell_info_dbus_free(struct ril_cell_info_dbus *dbus)
DBG("%s", dbus->path);
g_dbus_unregister_interface(dbus->conn, dbus->path,
RIL_CELL_INFO_DBUS_INTERFACE);
ofono_modem_remove_interface(dbus->md->ofono,
RIL_CELL_INFO_DBUS_INTERFACE);
/* Unregister cells */
l = dbus->entries;

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2016 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -14,10 +14,14 @@
*/
#include "ril_config.h"
#include "ril_log.h"
#include <gutil_intarray.h>
#include <gutil_ints.h>
/* Utilities for parsing ril_subscription.conf */
char* ril_config_get_string(GKeyFile *file, const char *group, const char *key)
char *ril_config_get_string(GKeyFile *file, const char *group, const char *key)
{
char *val = g_key_file_get_string(file, group, key, NULL);
@@ -29,6 +33,31 @@ char* ril_config_get_string(GKeyFile *file, const char *group, const char *key)
return val;
}
char **ril_config_get_strings(GKeyFile *file, const char *group,
const char *key, char delimiter)
{
char *str = ril_config_get_string(file, group, key);
if (str) {
char **strv, **p;
char delimiter_str[2];
delimiter_str[0] = delimiter;
delimiter_str[1] = 0;
strv = g_strsplit(str, delimiter_str, -1);
/* Strip whitespaces */
for (p = strv; *p; p++) {
*p = g_strstrip(*p);
}
g_free(str);
return strv;
}
return NULL;
}
gboolean ril_config_get_integer(GKeyFile *file, const char *group,
const char *key, int *out_value)
{
@@ -106,6 +135,95 @@ gboolean ril_config_get_flag(GKeyFile *file, const char *group,
}
}
gboolean ril_config_get_enum(GKeyFile *file, const char *group,
const char *key, int *result,
const char *name, int value, ...)
{
char *str = ril_config_get_string(file, group, key);
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;
}
GUtilInts *ril_config_get_ints(GKeyFile *file, const char *group,
const char *key)
{
char *value = ril_config_get_string(file, group, key);
if (value) {
char **values = g_strsplit(value, ",", -1);
char **ptr = values;
GUtilIntArray *array = gutil_int_array_new();
while (*ptr) {
const char *str = *ptr++;
char *end = NULL;
long ival = strtol(str, &end, 0);
if (str[0] && !end[0]) {
gutil_int_array_append(array, ival);
}
}
g_free(value);
g_strfreev(values);
return gutil_int_array_free_to_ints(array);
}
return NULL;
}
char *ril_config_ints_to_string(GUtilInts *ints, char separator)
{
if (ints) {
guint i, n;
const int *data = gutil_ints_get_data(ints, &n);
GString *buf = g_string_new(NULL);
for (i=0; i<n; i++) {
if (buf->len > 0) {
g_string_append_c(buf, separator);
}
g_string_append_printf(buf, "%d", data[i]);
}
return g_string_free(buf, FALSE);
}
return NULL;
}
/*
* Local Variables:
* mode: C

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2016 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -22,13 +22,22 @@
#define RILCONF_SETTINGS_GROUP "Settings"
char* ril_config_get_string(GKeyFile *file, const char *group, const char *key);
char *ril_config_get_string(GKeyFile *file, const char *group,
const char *key);
char **ril_config_get_strings(GKeyFile *file, const char *group,
const char *key, char delimiter);
gboolean ril_config_get_integer(GKeyFile *file, const char *group,
const char *key, int *value);
gboolean ril_config_get_boolean(GKeyFile *file, const char *group,
const char *key, gboolean *value);
gboolean ril_config_get_flag(GKeyFile *file, const char *group,
const char *key, int flag, int *flags);
gboolean ril_config_get_enum(GKeyFile *file, const char *group,
const char *key, int *result,
const char *name, int value, ...);
GUtilInts *ril_config_get_ints(GKeyFile *file, const char *group,
const char *key);
char *ril_config_ints_to_string(GUtilInts *ints, char separator);
#endif /* RIL_CONFIG_H */

View File

@@ -1,11 +1,10 @@
/*
*
* RIL constants adopted from AOSP's header:
*
* /hardware/ril/reference_ril/ril.h
*
* Copyright (C) 2013 Canonical Ltd.
* Copyright (C) 2013-2016 Jolla Ltd.
* Copyright (C) 2013-2017 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
@@ -20,37 +19,70 @@
#ifndef __RIL_CONSTANTS_H
#define __RIL_CONSTANTS_H 1
#define RIL_MAX_UUID_LENGTH 64
/* Error Codes */
#define RIL_E_SUCCESS 0
#define RIL_E_RADIO_NOT_AVAILABLE 1
#define RIL_E_GENERIC_FAILURE 2
#define RIL_E_PASSWORD_INCORRECT 3
#define RIL_E_SIM_PIN2 4
#define RIL_E_SIM_PUK2 5
#define RIL_E_REQUEST_NOT_SUPPORTED 6
#define RIL_E_CANCELLED 7
#define RIL_E_OP_NOT_ALLOWED_DURING_VOICE_CALL 8
#define RIL_E_OP_NOT_ALLOWED_BEFORE_REG_TO_NW 9
#define RIL_E_SMS_SEND_FAIL_RETRY 10
#define RIL_E_SIM_ABSENT 11
#define RIL_E_SUBSCRIPTION_NOT_AVAILABLE 12
#define RIL_E_MODE_NOT_SUPPORTED 13
#define RIL_E_FDN_CHECK_FAILURE 14
#define RIL_E_ILLEGAL_SIM_OR_ME 15
#define RIL_E_UNUSED 16
#define RIL_E_DIAL_MODIFIED_TO_USSD 17
#define RIL_E_DIAL_MODIFIED_TO_SS 18
#define RIL_E_DIAL_MODIFIED_TO_DIAL 19
#define RIL_E_USSD_MODIFIED_TO_DIAL 20
#define RIL_E_USSD_MODIFIED_TO_SS 21
#define RIL_E_USSD_MODIFIED_TO_USSD 22
#define RIL_E_SS_MODIFIED_TO_DIAL 23
#define RIL_E_SS_MODIFIED_TO_USSD 24
#define RIL_E_SS_MODIFIED_TO_SS 25
#define RIL_E_SUBSCRIPTION_NOT_SUPPORTED 26
#define RIL_E_MISSING_RESOURCE 27
#define RIL_E_NO_SUCH_ELEMENT 28
#define RIL_E_INVALID_PARAMETER 29
enum ril_status {
RIL_E_SUCCESS = 0,
RIL_E_RADIO_NOT_AVAILABLE = 1,
RIL_E_GENERIC_FAILURE = 2,
RIL_E_PASSWORD_INCORRECT = 3,
RIL_E_SIM_PIN2 = 4,
RIL_E_SIM_PUK2 = 5,
RIL_E_REQUEST_NOT_SUPPORTED = 6,
RIL_E_CANCELLED = 7,
RIL_E_OP_NOT_ALLOWED_DURING_VOICE_CALL = 8,
RIL_E_OP_NOT_ALLOWED_BEFORE_REG_TO_NW = 9,
RIL_E_SMS_SEND_FAIL_RETRY = 10,
RIL_E_SIM_ABSENT = 11,
RIL_E_SUBSCRIPTION_NOT_AVAILABLE = 12,
RIL_E_MODE_NOT_SUPPORTED = 13,
RIL_E_FDN_CHECK_FAILURE = 14,
RIL_E_ILLEGAL_SIM_OR_ME = 15,
RIL_E_MISSING_RESOURCE = 16,
RIL_E_NO_SUCH_ELEMENT = 17,
RIL_E_DIAL_MODIFIED_TO_USSD = 18,
RIL_E_DIAL_MODIFIED_TO_SS = 19,
RIL_E_DIAL_MODIFIED_TO_DIAL = 20,
RIL_E_USSD_MODIFIED_TO_DIAL = 21,
RIL_E_USSD_MODIFIED_TO_SS = 22,
RIL_E_USSD_MODIFIED_TO_USSD = 23,
RIL_E_SS_MODIFIED_TO_DIAL = 24,
RIL_E_SS_MODIFIED_TO_USSD = 25,
RIL_E_SUBSCRIPTION_NOT_SUPPORTED = 26,
RIL_E_SS_MODIFIED_TO_SS = 27,
RIL_E_LCE_NOT_SUPPORTED = 36,
RIL_E_NO_MEMORY = 37,
RIL_E_INTERNAL_ERR = 38,
RIL_E_SYSTEM_ERR = 39,
RIL_E_MODEM_ERR = 40,
RIL_E_INVALID_STATE = 41,
RIL_E_NO_RESOURCES = 42,
RIL_E_SIM_ERR = 43,
RIL_E_INVALID_ARGUMENTS = 44,
RIL_E_INVALID_SIM_STATE = 45,
RIL_E_INVALID_MODEM_STATE = 46,
RIL_E_INVALID_CALL_ID = 47,
RIL_E_NO_SMS_TO_ACK = 48,
RIL_E_NETWORK_ERR = 49,
RIL_E_REQUEST_RATE_LIMITED = 50,
RIL_E_SIM_BUSY = 51,
RIL_E_SIM_FULL = 52,
RIL_E_NETWORK_REJECT = 53,
RIL_E_OPERATION_NOT_ALLOWED = 54,
RIL_E_EMPTY_RECORD = 55,
RIL_E_INVALID_SMS_FORMAT = 56,
RIL_E_ENCODING_ERR = 57,
RIL_E_INVALID_SMSC_ADDRESS = 58,
RIL_E_NO_SUCH_ENTRY = 59,
RIL_E_NETWORK_NOT_READY = 60,
RIL_E_NOT_PROVISIONED = 61,
RIL_E_NO_SUBSCRIPTION = 62,
RIL_E_NO_NETWORK_FOUND = 63,
RIL_E_DEVICE_IN_USE = 64,
RIL_E_ABORTED = 65,
RIL_E_INVALID_RESPONSE = 66
};
/* call states */
enum ril_call_state {
@@ -114,49 +146,125 @@ enum ril_radio_tech {
RADIO_TECH_HSPAP = 15,
RADIO_TECH_GSM = 16,
RADIO_TECH_TD_SCDMA = 17,
RADIO_TECH_IWLAN = 18
RADIO_TECH_IWLAN = 18,
RADIO_TECH_LTE_CA = 19
};
/* Radio capabilities */
enum ril_radio_access_family {
RAF_GPRS = (1 << RADIO_TECH_GPRS),
RAF_EDGE = (1 << RADIO_TECH_EDGE),
RAF_UMTS = (1 << RADIO_TECH_UMTS),
RAF_IS95A = (1 << RADIO_TECH_IS95A),
RAF_IS95B = (1 << RADIO_TECH_IS95B),
RAF_1xRTT = (1 << RADIO_TECH_1xRTT),
RAF_EVDO_0 = (1 << RADIO_TECH_EVDO_0),
RAF_EVDO_A = (1 << RADIO_TECH_EVDO_A),
RAF_HSDPA = (1 << RADIO_TECH_HSDPA),
RAF_HSUPA = (1 << RADIO_TECH_HSUPA),
RAF_HSPA = (1 << RADIO_TECH_HSPA),
RAF_EVDO_B = (1 << RADIO_TECH_EVDO_B),
RAF_EHRPD = (1 << RADIO_TECH_EHRPD),
RAF_LTE = (1 << RADIO_TECH_LTE),
RAF_HSPAP = (1 << RADIO_TECH_HSPAP),
RAF_GSM = (1 << RADIO_TECH_GSM),
RAF_TD_SCDMA = (1 << RADIO_TECH_TD_SCDMA),
RAF_LTE_CA = (1 << RADIO_TECH_LTE_CA)
};
enum ril_radio_capability_phase {
RC_PHASE_CONFIGURED = 0,
RC_PHASE_START = 1,
RC_PHASE_APPLY = 2,
RC_PHASE_UNSOL_RSP = 3,
RC_PHASE_FINISH = 4
};
enum ril_radio_capability_status {
RC_STATUS_NONE = 0,
RC_STATUS_SUCCESS = 1,
RC_STATUS_FAIL = 2
};
#define RIL_RADIO_CAPABILITY_VERSION 1
struct ril_radio_capability {
int version;
int session;
enum ril_radio_capability_phase phase;
enum ril_radio_access_family rat;
char logicalModemUuid[RIL_MAX_UUID_LENGTH];
int status;
};
enum ril_uicc_subscription_action {
RIL_UICC_SUBSCRIPTION_DEACTIVATE = 0,
RIL_UICC_SUBSCRIPTION_ACTIVATE = 1
};
/* See RIL_REQUEST_LAST_CALL_FAIL_CAUSE */
#define CALL_FAIL_UNOBTAINABLE_NUMBER 1
#define CALL_FAIL_NORMAL 16
#define CALL_FAIL_BUSY 17
#define CALL_FAIL_CONGESTION 34
#define CALL_FAIL_ACM_LIMIT_EXCEEDED 68
#define CALL_FAIL_CALL_BARRED 240
#define CALL_FAIL_FDN_BLOCKED 241
#define CALL_FAIL_IMSI_UNKNOWN_IN_VLR 242
#define CALL_FAIL_IMEI_NOT_ACCEPTED 243
#define CALL_FAIL_DIAL_MODIFIED_TO_USSD 244
#define CALL_FAIL_DIAL_MODIFIED_TO_SS 245
#define CALL_FAIL_DIAL_MODIFIED_TO_DIAL 246
#define CALL_FAIL_CDMA_LOCKED_UNTIL_POWER_CYCLE 1000
#define CALL_FAIL_CDMA_DROP 1001
#define CALL_FAIL_CDMA_INTERCEPT 1002
#define CALL_FAIL_CDMA_REORDER 1003
#define CALL_FAIL_CDMA_SO_REJECT 1004
#define CALL_FAIL_CDMA_RETRY_ORDER 1005
#define CALL_FAIL_CDMA_ACCESS_FAILURE 1006
#define CALL_FAIL_CDMA_PREEMPTED 1007
#define CALL_FAIL_CDMA_NOT_EMERGENCY 1008
#define CALL_FAIL_CDMA_ACCESS_BLOCKED 1009
#define CALL_FAIL_ERROR_UNSPECIFIED 0xffff
enum ril_call_fail_cause {
CALL_FAIL_UNOBTAINABLE_NUMBER = 1,
CALL_FAIL_NO_ROUTE_TO_DESTINATION = 3,
CALL_FAIL_CHANNEL_UNACCEPTABLE = 6,
CALL_FAIL_OPERATOR_DETERMINED_BARRING = 8,
CALL_FAIL_NORMAL = 16,
CALL_FAIL_BUSY = 17,
CALL_FAIL_NO_USER_RESPONDING = 18,
CALL_FAIL_NO_ANSWER_FROM_USER = 19,
CALL_FAIL_CALL_REJECTED = 21,
CALL_FAIL_NUMBER_CHANGED = 22,
CALL_FAIL_DESTINATION_OUT_OF_ORDER = 27,
CALL_FAIL_INVALID_NUMBER_FORMAT = 28,
CALL_FAIL_FACILITY_REJECTED = 29,
CALL_FAIL_RESP_TO_STATUS_ENQUIRY = 30,
CALL_FAIL_NORMAL_UNSPECIFIED = 31,
CALL_FAIL_CONGESTION = 34,
CALL_FAIL_NETWORK_OUT_OF_ORDER = 38,
CALL_FAIL_TEMPORARY_FAILURE = 41,
CALL_FAIL_SWITCHING_EQUIPMENT_CONGESTION = 42,
CALL_FAIL_ACCESS_INFORMATION_DISCARDED = 43,
CALL_FAIL_REQUESTED_CIRCUIT_OR_CHANNEL_NOT_AVAILABLE = 44,
CALL_FAIL_RESOURCES_UNAVAILABLE_OR_UNSPECIFIED = 47,
CALL_FAIL_QOS_UNAVAILABLE = 49,
CALL_FAIL_REQUESTED_FACILITY_NOT_SUBSCRIBED = 50,
CALL_FAIL_INCOMING_CALLS_BARRED_WITHIN_CUG = 55,
CALL_FAIL_BEARER_CAPABILITY_NOT_AUTHORIZED = 57,
CALL_FAIL_BEARER_CAPABILITY_UNAVAILABLE = 58,
CALL_FAIL_SERVICE_OPTION_NOT_AVAILABLE = 63,
CALL_FAIL_BEARER_SERVICE_NOT_IMPLEMENTED = 65,
CALL_FAIL_ACM_LIMIT_EXCEEDED = 68,
CALL_FAIL_REQUESTED_FACILITY_NOT_IMPLEMENTED = 69,
CALL_FAIL_ONLY_DIGITAL_INFORMATION_BEARER_AVAILABLE = 70,
CALL_FAIL_SERVICE_OR_OPTION_NOT_IMPLEMENTED = 79,
CALL_FAIL_INVALID_TRANSACTION_IDENTIFIER = 81,
CALL_FAIL_USER_NOT_MEMBER_OF_CUG = 87,
CALL_FAIL_INCOMPATIBLE_DESTINATION = 88,
CALL_FAIL_INVALID_TRANSIT_NW_SELECTION = 91,
CALL_FAIL_SEMANTICALLY_INCORRECT_MESSAGE = 95,
CALL_FAIL_INVALID_MANDATORY_INFORMATION = 96,
CALL_FAIL_MESSAGE_TYPE_NON_IMPLEMENTED = 97,
CALL_FAIL_MESSAGE_TYPE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 98,
CALL_FAIL_INFORMATION_ELEMENT_NON_EXISTENT = 99,
CALL_FAIL_CONDITIONAL_IE_ERROR = 100,
CALL_FAIL_MESSAGE_NOT_COMPATIBLE_WITH_PROTOCOL_STATE = 101,
CALL_FAIL_RECOVERY_ON_TIMER_EXPIRED = 102,
CALL_FAIL_PROTOCOL_ERROR_UNSPECIFIED = 111,
CALL_FAIL_INTERWORKING_UNSPECIFIED = 127,
CALL_FAIL_CALL_BARRED = 240,
CALL_FAIL_FDN_BLOCKED = 241,
CALL_FAIL_IMSI_UNKNOWN_IN_VLR = 242,
CALL_FAIL_IMEI_NOT_ACCEPTED = 243,
CALL_FAIL_DIAL_MODIFIED_TO_USSD = 244,
CALL_FAIL_DIAL_MODIFIED_TO_SS = 245,
CALL_FAIL_DIAL_MODIFIED_TO_DIAL = 246,
CALL_FAIL_ERROR_UNSPECIFIED = 0xffff,
/* Not defined in ril.h but valid 3GPP specific cause values
* for call control. See 3GPP TS 24.008 Annex H. */
#define CALL_FAIL_NO_ROUTE_TO_DESTINATION 3
#define CALL_FAIL_CHANNEL_UNACCEPTABLE 6
#define CALL_FAIL_OPERATOR_DETERMINED_BARRING 8
#define CALL_FAIL_NO_USER_RESPONDING 18
#define CALL_FAIL_USER_ALERTING_NO_ANSWER 19
#define CALL_FAIL_CALL_REJECTED 21
#define CALL_FAIL_NUMBER_CHANGED 22
#define CALL_FAIL_ANONYMOUS_CALL_REJECTION 24
#define CALL_FAIL_PRE_EMPTION 25
#define CALL_FAIL_DESTINATION_OUT_OF_ORDER 27
#define CALL_FAIL_INCOMPLETE_NUMBER 28
#define CALL_FAIL_FACILITY_REJECTED 29
#define CALL_FAIL_NORMAL_UNSPECIFIED 31
CALL_FAIL_ANONYMOUS_CALL_REJECTION = 24,
CALL_FAIL_PRE_EMPTION = 25
};
enum ril_data_call_fail_cause {
PDP_FAIL_NONE = 0,

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2016 Jolla Ltd.
* Copyright (C) 2016-2017 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
@@ -27,15 +27,14 @@
#include <grilio_parser.h>
#include <grilio_request.h>
#define SETUP_DATA_CALL_PARAMS 7
#define DATA_PROFILE_DEFAULT_STR "0"
#define DEACTIVATE_DATA_CALL_PARAMS 2
#define PROTO_IP_STR "IP"
#define PROTO_IPV6_STR "IPV6"
#define PROTO_IPV4V6_STR "IPV4V6"
enum ril_data_priv_flags {
RIL_DATA_FLAG_NONE = 0x00,
RIL_DATA_FLAG_ALLOWED = 0x01,
RIL_DATA_FLAG_MAX_SPEED = 0x02,
RIL_DATA_FLAG_ON = 0x04
@@ -100,6 +99,7 @@ struct ril_data_priv {
struct ril_data_request *pending_req;
struct ril_data_options options;
guint slot;
char *log_prefix;
guint query_id;
gulong io_event_id;
@@ -168,9 +168,9 @@ struct ril_data_request_deact {
int cid;
};
struct ril_data_request_2g {
struct ril_data_request_allow_data {
struct ril_data_request req;
gulong handler_id;
gboolean allow;
};
static void ril_data_manager_check_data(struct ril_data_manager *dm);
@@ -182,6 +182,26 @@ static void ril_data_signal_emit(struct ril_data *self, enum ril_data_signal id)
g_signal_emit(self, ril_data_signals[id], 0);
}
/*==========================================================================*
* RIL requests
*==========================================================================*/
GRilIoRequest *ril_request_allow_data_new(gboolean allow)
{
return grilio_request_array_int32_new(1, allow);
}
GRilIoRequest *ril_request_deactivate_data_call_new(int cid)
{
GRilIoRequest *req = grilio_request_new();
grilio_request_append_int32(req, 2 /* Parameter count */);
grilio_request_append_format(req, "%d", cid);
grilio_request_append_format(req, "%d",
RIL_DEACTIVATE_DATA_CALL_NO_REASON);
return req;
}
/*==========================================================================*
* ril_data_call
*==========================================================================*/
@@ -785,8 +805,8 @@ static gboolean ril_data_call_setup_submit(struct ril_data_request *req)
G_CAST(req, struct ril_data_request_setup, req);
struct ril_data_priv *priv = req->data->priv;
const char *proto_str = ril_data_ofono_protocol_to_ril(setup->proto);
GRilIoRequest* ioreq;
int tech, auth;
GRilIoRequest *ioreq;
int tech, auth = RIL_AUTH_NONE;
GASSERT(proto_str);
@@ -809,21 +829,29 @@ static gboolean ril_data_call_setup_submit(struct ril_data_request *req)
tech = RADIO_TECH_HSPA;
}
/*
* We do the same as in $AOSP/frameworks/opt/telephony/src/java/com/
* android/internal/telephony/dataconnection/DataConnection.java,
* onConnect(), and use authentication or not depending on whether
* the user field is empty or not.
*/
auth = (setup->username && setup->username[0]) ?
RIL_AUTH_BOTH : RIL_AUTH_NONE;
if (setup->username && setup->username[0]) {
switch (setup->auth_method) {
case OFONO_GPRS_AUTH_METHOD_ANY:
auth = RIL_AUTH_BOTH;
break;
case OFONO_GPRS_AUTH_METHOD_NONE:
auth = RIL_AUTH_NONE;
break;
case OFONO_GPRS_AUTH_METHOD_CHAP:
auth = RIL_AUTH_CHAP;
break;
case OFONO_GPRS_AUTH_METHOD_PAP:
auth = RIL_AUTH_PAP;
break;
}
}
/*
* TODO: add comments about tethering, other non-public
* profiles...
*/
ioreq = grilio_request_new();
grilio_request_append_int32(ioreq, SETUP_DATA_CALL_PARAMS);
grilio_request_append_int32(ioreq, 7 /* Parameter count */);
grilio_request_append_format(ioreq, "%d", tech);
grilio_request_append_utf8(ioreq, DATA_PROFILE_DEFAULT_STR);
grilio_request_append_utf8(ioreq, setup->apn);
@@ -922,6 +950,10 @@ static void ril_data_call_deact_cb(GRilIoChannel *io, int ril_status,
ril_data_call_free(call);
ril_data_signal_emit(data, SIGNAL_CALLS_CHANGED);
}
} else {
/* Something seems to be slightly broken, request the
* current state */
ril_data_poll_call_state(data);
}
if (req->cb.deact) {
@@ -936,12 +968,8 @@ static gboolean ril_data_call_deact_submit(struct ril_data_request *req)
struct ril_data_request_deact *deact =
G_CAST(req, struct ril_data_request_deact, req);
struct ril_data_priv *priv = req->data->priv;
GRilIoRequest* ioreq = grilio_request_new();
grilio_request_append_int32(ioreq, DEACTIVATE_DATA_CALL_PARAMS);
grilio_request_append_format(ioreq, "%d", deact->cid);
grilio_request_append_format(ioreq, "%d",
RIL_DEACTIVATE_DATA_CALL_NO_REASON);
GRilIoRequest *ioreq =
ril_request_deactivate_data_call_new(deact->cid);
req->pending_id = grilio_queue_send_request_full(priv->q, ioreq,
RIL_REQUEST_DEACTIVATE_DATA_CALL,
@@ -973,15 +1001,6 @@ static struct ril_data_request *ril_data_call_deact_new(struct ril_data *data,
* ril_data_allow_request
*==========================================================================*/
static GRilIoRequest *ril_data_allow_req(gboolean allow)
{
GRilIoRequest *req = grilio_request_sized_new(8);
grilio_request_append_int32(req, 1);
grilio_request_append_int32(req, allow != FALSE);
return req;
}
static void ril_data_allow_cb(GRilIoChannel *io, int ril_status,
const void *req_data, guint len, void *user_data)
{
@@ -991,13 +1010,22 @@ static void ril_data_allow_cb(GRilIoChannel *io, int ril_status,
ril_data_request_completed(req);
if (ril_status == RIL_E_SUCCESS &&
(priv->flags & RIL_DATA_FLAG_ALLOWED)) {
GASSERT(!ril_data_allowed(data));
priv->flags |= RIL_DATA_FLAG_ON;
GASSERT(ril_data_allowed(data));
DBG_(data, "data on");
ril_data_signal_emit(data, SIGNAL_ALLOW_CHANGED);
if (ril_status == RIL_E_SUCCESS) {
const gboolean was_allowed = ril_data_allowed(data);
struct ril_data_request_allow_data *ad =
G_CAST(req, struct ril_data_request_allow_data, req);
if (ad->allow) {
priv->flags |= RIL_DATA_FLAG_ON;
DBG_(data, "data on");
} else {
priv->flags &= ~RIL_DATA_FLAG_ON;
DBG_(data, "data off");
}
if (ril_data_allowed(data) != was_allowed) {
ril_data_signal_emit(data, SIGNAL_ALLOW_CHANGED);
}
}
ril_data_request_finish(req);
@@ -1005,25 +1033,32 @@ static void ril_data_allow_cb(GRilIoChannel *io, int ril_status,
static gboolean ril_data_allow_submit(struct ril_data_request *req)
{
GRilIoRequest *ioreq = ril_data_allow_req(TRUE);
struct ril_data_request_allow_data *ad =
G_CAST(req, struct ril_data_request_allow_data, req);
GRilIoRequest *ioreq = ril_request_allow_data_new(ad->allow);
struct ril_data_priv *priv = req->data->priv;
grilio_request_set_retry(ioreq, RIL_RETRY_SECS*1000, -1);
grilio_request_set_blocking(ioreq, TRUE);
req->pending_id = grilio_queue_send_request_full(priv->q, ioreq,
RIL_REQUEST_ALLOW_DATA, ril_data_allow_cb, NULL, req);
grilio_request_unref(ioreq);
return TRUE;
}
static struct ril_data_request *ril_data_allow_new(struct ril_data *data)
static struct ril_data_request *ril_data_allow_new(struct ril_data *data,
gboolean allow)
{
struct ril_data_request *req = g_new0(struct ril_data_request, 1);
struct ril_data_request_allow_data *ad =
g_new0(struct ril_data_request_allow_data, 1);
struct ril_data_request *req = &ad->req;
req->name = "ALLOW_DATA";
req->data = data;
req->submit = ril_data_allow_submit;
req->cancel = ril_data_request_cancel_io;
req->flags = DATA_REQUEST_FLAG_CANCEL_WHEN_DISALLOWED;
ad->allow = allow;
return req;
}
@@ -1058,21 +1093,31 @@ static void ril_data_settings_changed(struct ril_sim_settings *settings,
ril_data_manager_check_network_mode(RIL_DATA(user_data)->priv->dm);
}
static gint ril_data_compare_cb(gconstpointer a, gconstpointer b)
{
const struct ril_data *d1 = a;
const struct ril_data *d2 = b;
const struct ril_data_priv *p1 = d1->priv;
const struct ril_data_priv *p2 = d2->priv;
return p1->slot < p2->slot ? (-1) : p1->slot > p2->slot ? 1 : 0;
}
struct ril_data *ril_data_new(struct ril_data_manager *dm, const char *name,
struct ril_radio *radio, struct ril_network *network,
GRilIoChannel *io, const struct ril_data_options *options)
GRilIoChannel *io, const struct ril_data_options *options,
const struct ril_slot_config *config)
{
GASSERT(dm);
if (G_LIKELY(dm)) {
struct ril_data *self = g_object_new(RIL_DATA_TYPE, NULL);
struct ril_data_priv *priv = self->priv;
struct ril_sim_settings *settings = network->settings;
GRilIoRequest *req = grilio_request_new();
priv->options = *options;
switch (priv->options.allow_data) {
case RIL_ALLOW_DATA_ON:
case RIL_ALLOW_DATA_OFF:
case RIL_ALLOW_DATA_ENABLED:
case RIL_ALLOW_DATA_DISABLED:
break;
default:
/*
@@ -1080,13 +1125,15 @@ struct ril_data *ril_data_new(struct ril_data_manager *dm, const char *name,
* RIL_VERSION was 10
*/
priv->options.allow_data = (io->ril_version > 10) ?
RIL_ALLOW_DATA_ON : RIL_ALLOW_DATA_OFF;
RIL_ALLOW_DATA_ENABLED :
RIL_ALLOW_DATA_DISABLED;
break;
}
priv->log_prefix = (name && name[0]) ?
g_strconcat(name, " ", NULL) : g_strdup("");
priv->slot = config->slot;
priv->q = grilio_queue_new(io);
priv->io = grilio_channel_ref(io);
priv->dm = ril_data_manager_ref(dm);
@@ -1104,20 +1151,36 @@ struct ril_data *ril_data_new(struct ril_data_manager *dm, const char *name,
ril_data_settings_changed, self);
/* Request the current state */
grilio_request_set_retry(req, RIL_RETRY_SECS*1000, -1);
priv->query_id = grilio_queue_send_request_full(priv->q, req,
RIL_REQUEST_DATA_CALL_LIST,
ril_data_query_data_calls_cb,
NULL, self);
grilio_request_unref(req);
ril_data_poll_call_state(self);
dm->data_list = g_slist_append(dm->data_list, self);
/* Order data contexts according to slot numbers */
dm->data_list = g_slist_insert_sorted(dm->data_list, self,
ril_data_compare_cb);
ril_data_manager_check_network_mode(dm);
return self;
}
return NULL;
}
void ril_data_poll_call_state(struct ril_data *self)
{
if (G_LIKELY(self)) {
struct ril_data_priv *priv = self->priv;
if (!priv->query_id) {
GRilIoRequest *req = grilio_request_new();
grilio_request_set_retry(req, RIL_RETRY_SECS*1000, -1);
priv->query_id =
grilio_queue_send_request_full(priv->q, req,
RIL_REQUEST_DATA_CALL_LIST,
ril_data_query_data_calls_cb,
NULL, self);
grilio_request_unref(req);
}
}
}
struct ril_data *ril_data_ref(struct ril_data *self)
{
if (G_LIKELY(self)) {
@@ -1195,10 +1258,11 @@ static void ril_data_cancel_requests(struct ril_data *self,
static void ril_data_disallow(struct ril_data *self)
{
struct ril_data_priv *priv = self->priv;
const gboolean was_allowed = ril_data_allowed(self);
DBG_(self, "disallowed");
GASSERT(priv->flags & RIL_DATA_FLAG_ALLOWED);
priv->flags &= ~(RIL_DATA_FLAG_ALLOWED | RIL_DATA_FLAG_ON);
priv->flags &= ~RIL_DATA_FLAG_ALLOWED;
/*
* Cancel all requests that can be canceled.
@@ -1211,7 +1275,20 @@ static void ril_data_disallow(struct ril_data *self)
* requests are already pending? That's quite unlikely though)
*/
ril_data_deactivate_all(self);
ril_data_power_update(self);
if (priv->options.allow_data == RIL_ALLOW_DATA_ENABLED) {
/* Tell rild that the data is now disabled */
ril_data_request_queue(ril_data_allow_new(self, FALSE));
} else {
priv->flags &= ~RIL_DATA_FLAG_ON;
GASSERT(!ril_data_allowed(self));
DBG_(self, "data off");
ril_data_power_update(self);
}
if (ril_data_allowed(self) != was_allowed) {
ril_data_signal_emit(self, SIGNAL_ALLOW_CHANGED);
}
}
static void ril_data_max_speed_cb(gpointer data, gpointer max_speed)
@@ -1228,12 +1305,7 @@ static void ril_data_disallow_cb(gpointer data_ptr, gpointer allowed)
struct ril_data *data = data_ptr;
if (data->priv->flags & RIL_DATA_FLAG_ALLOWED) {
const gboolean was_allowed = ril_data_allowed(data);
ril_data_disallow(data);
if (was_allowed) {
ril_data_signal_emit(data,
SIGNAL_ALLOW_CHANGED);
}
}
}
}
@@ -1287,13 +1359,7 @@ void ril_data_allow(struct ril_data *self, enum ril_data_role role)
}
} else {
if (priv->flags & RIL_DATA_FLAG_ALLOWED) {
gboolean was_allowed = ril_data_allowed(self);
ril_data_disallow(self);
if (was_allowed) {
ril_data_signal_emit(self,
SIGNAL_ALLOW_CHANGED);
}
ril_data_manager_check_data(dm);
}
}
@@ -1444,53 +1510,54 @@ static void ril_data_manager_check_network_mode(struct ril_data_manager *self)
GSList *l;
if (ril_data_manager_handover(self)) {
gboolean need_fast_access = FALSE;
struct ril_network *lte_network = NULL;
int non_gsm_count = 0;
/*
* Count number of SIMs for which GSM is selected
* Count number of SIMs for which non-GSM mode is selected
*/
for (l= self->data_list; l; l = l->next) {
struct ril_data *data = l->data;
struct ril_data_priv *priv = data->priv;
struct ril_sim_settings *sim = priv->network->settings;
struct ril_network *network = priv->network;
struct ril_sim_settings *sim = network->settings;
if (sim->pref_mode != OFONO_RADIO_ACCESS_MODE_GSM &&
sim->imsi) {
if (sim->pref_mode != OFONO_RADIO_ACCESS_MODE_GSM) {
non_gsm_count++;
if (priv->flags & RIL_DATA_FLAG_MAX_SPEED) {
need_fast_access = TRUE;
if ((priv->flags & RIL_DATA_FLAG_MAX_SPEED) &&
!lte_network) {
lte_network = network;
}
}
}
/*
* If the SIM selected for internet access has non-GSM mode
* enabled and non-GSM mode is enabled for more than one SIM,
* then we need to limit other SIMs to GSM. Otherwise, turn
* all limits off.
* If there's no SIM selected for internet access
* then choose the first slot for LTE.
*/
if (need_fast_access && non_gsm_count > 1) {
for (l= self->data_list; l; l = l->next) {
struct ril_data *data = l->data;
struct ril_data_priv *priv = data->priv;
ril_network_set_max_pref_mode(priv->network,
(priv->flags & RIL_DATA_FLAG_MAX_SPEED) ?
OFONO_RADIO_ACCESS_MODE_ANY :
OFONO_RADIO_ACCESS_MODE_GSM,
FALSE);
}
return;
if (!lte_network) {
struct ril_data *data = self->data_list->data;
lte_network = data->priv->network;
}
}
/* Otherwise there's no reason to limit anything */
for (l= self->data_list; l; l = l->next) {
struct ril_data *data = l->data;
ril_network_set_max_pref_mode(data->priv->network,
for (l= self->data_list; l; l = l->next) {
struct ril_data *data = l->data;
struct ril_network *network = data->priv->network;
ril_network_set_max_pref_mode(network,
(network == lte_network) ?
OFONO_RADIO_ACCESS_MODE_ANY :
OFONO_RADIO_ACCESS_MODE_GSM,
FALSE);
}
} else {
/* Otherwise there's no reason to limit anything */
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);
}
}
}
@@ -1521,9 +1588,8 @@ static void ril_data_manager_switch_data_on(struct ril_data_manager *self,
OFONO_RADIO_ACCESS_MODE_ANY, TRUE);
}
if (priv->options.allow_data == RIL_ALLOW_DATA_ON) {
ril_data_request_queue(ril_data_allow_new(data));
if (priv->options.allow_data == RIL_ALLOW_DATA_ENABLED) {
ril_data_request_queue(ril_data_allow_new(data, TRUE));
} else {
priv->flags |= RIL_DATA_FLAG_ON;
GASSERT(ril_data_allowed(data));
@@ -1546,6 +1612,16 @@ static void ril_data_manager_check_data(struct ril_data_manager *self)
}
}
void ril_data_manager_assert_data_on(struct ril_data_manager *self)
{
if (self) {
struct ril_data *data = ril_data_manager_allowed(self);
if (data) {
ril_data_request_queue(ril_data_allow_new(data, TRUE));
}
}
}
/*
* Local Variables:
* mode: C

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2016 Jolla Ltd.
* Copyright (C) 2016-2017 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
@@ -56,8 +56,8 @@ enum ril_data_manager_flags {
enum ril_data_allow_data_opt {
RIL_ALLOW_DATA_AUTO,
RIL_ALLOW_DATA_ON,
RIL_ALLOW_DATA_OFF
RIL_ALLOW_DATA_ENABLED,
RIL_ALLOW_DATA_DISABLED
};
enum ril_data_call_format {
@@ -84,6 +84,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_assert_data_on(struct ril_data_manager *dm);
typedef void (*ril_data_cb_t)(struct ril_data *data, void *arg);
typedef void (*ril_data_call_setup_cb_t)(struct ril_data *data,
@@ -94,10 +95,12 @@ typedef void (*ril_data_call_deactivate_cb_t)(struct ril_data *data,
struct ril_data *ril_data_new(struct ril_data_manager *dm, const char *name,
struct ril_radio *radio, struct ril_network *network,
GRilIoChannel *io, const struct ril_data_options *options);
GRilIoChannel *io, const struct ril_data_options *options,
const struct ril_slot_config *config);
struct ril_data *ril_data_ref(struct ril_data *data);
void ril_data_unref(struct ril_data *data);
gboolean ril_data_allowed(struct ril_data *data);
void ril_data_poll_call_state(struct ril_data *data);
gulong ril_data_add_allow_changed_handler(struct ril_data *data,
ril_data_cb_t cb, void *arg);
@@ -121,6 +124,10 @@ struct ril_data_call *ril_data_call_dup(const struct ril_data_call *call);
struct ril_data_call *ril_data_call_find(struct ril_data_call_list *list,
int cid);
/* Constructors of various kinds of RIL requests */
GRilIoRequest *ril_request_allow_data_new(gboolean allow);
GRilIoRequest *ril_request_deactivate_data_call_new(int cid);
#endif /* RIL_DATA_H */
/*

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2016 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -17,16 +17,24 @@
#include "ril_util.h"
#include "ril_log.h"
#include <gutil_idlequeue.h>
/*
* TODO: No public RIL api to query manufacturer or model.
* Check where to get, could /system/build.prop be updated to have good values?
*/
enum ril_devinfo_cb_tag {
DEVINFO_QUERY_SERIAL = 1,
DEVINFO_QUERY_SVN
};
struct ril_devinfo {
struct ofono_devinfo *info;
GRilIoQueue *q;
guint register_id;
guint imei_id;
GUtilIdleQueue *iq;
char *log_prefix;
char *imeisv;
char *imei;
};
@@ -36,6 +44,7 @@ struct ril_devinfo_cbd {
gpointer data;
};
#define DBG_(self,fmt,args...) DBG("%s" fmt, (self)->log_prefix, ##args)
#define ril_devinfo_cbd_free g_free
static inline struct ril_devinfo *ril_devinfo_get_data(
@@ -62,7 +71,7 @@ static void ril_devinfo_query_unsupported(struct ofono_devinfo *info,
cb(ril_error_failure(&error), "", data);
}
static void ril_devinfo_query_cb(GRilIoChannel *io, int status,
static void ril_devinfo_query_revision_cb(GRilIoChannel *io, int status,
const void *data, guint len, void *user_data)
{
struct ofono_error error;
@@ -73,7 +82,7 @@ static void ril_devinfo_query_cb(GRilIoChannel *io, int status,
GRilIoParser rilp;
grilio_parser_init(&rilp, data, len);
res = grilio_parser_get_utf8(&rilp);
DBG("%s", res);
DBG_(cbd->di, "%s", res);
cbd->cb(ril_error_ok(&error), res ? res : "", cbd->data);
g_free(res);
} else {
@@ -86,23 +95,46 @@ static void ril_devinfo_query_revision(struct ofono_devinfo *info,
{
struct ril_devinfo *di = ril_devinfo_get_data(info);
DBG("");
grilio_queue_send_request_full(di->q, NULL, RIL_REQUEST_BASEBAND_VERSION,
ril_devinfo_query_cb, ril_devinfo_cbd_free,
DBG_(di, "");
grilio_queue_send_request_full(di->q, NULL,
RIL_REQUEST_BASEBAND_VERSION,
ril_devinfo_query_revision_cb,
ril_devinfo_cbd_free,
ril_devinfo_cbd_new(di, cb, data));
}
static gboolean ril_devinfo_query_serial_cb(void *user_data)
static void ril_devinfo_query_serial_cb(gpointer user_data)
{
struct ril_devinfo_cbd *cbd = user_data;
struct ril_devinfo *di = cbd->di;
struct ofono_error error;
GASSERT(di->imei_id);
di->imei_id = 0;
DBG_(di, "%s", di->imei);
cbd->cb(ril_error_ok(&error), di->imei, cbd->data);
return FALSE;
}
static void ril_devinfo_query_svn_cb(gpointer user_data)
{
struct ril_devinfo_cbd *cbd = user_data;
struct ril_devinfo *di = cbd->di;
struct ofono_error error;
DBG_(di, "%s", di->imeisv);
if (di->imeisv && di->imeisv[0]) {
cbd->cb(ril_error_ok(&error), di->imeisv, cbd->data);
} else {
cbd->cb(ril_error_failure(&error), "", cbd->data);
}
}
static void ril_devinfo_query(struct ril_devinfo *di,
enum ril_devinfo_cb_tag tag, GUtilIdleFunc fn,
ofono_devinfo_query_cb_t cb, void *data)
{
GVERIFY_FALSE(gutil_idle_queue_cancel_tag(di->iq, tag));
gutil_idle_queue_add_tag_full(di->iq, tag, fn,
ril_devinfo_cbd_new(di, cb, data),
ril_devinfo_cbd_free);
}
static void ril_devinfo_query_serial(struct ofono_devinfo *info,
@@ -111,29 +143,28 @@ static void ril_devinfo_query_serial(struct ofono_devinfo *info,
{
struct ril_devinfo *di = ril_devinfo_get_data(info);
GASSERT(!di->imei_id);
if (di->imei_id) {
g_source_remove(di->imei_id);
di->imei_id = 0;
}
DBG("%s", di->imei);
di->imei_id = g_idle_add_full(G_PRIORITY_DEFAULT_IDLE,
ril_devinfo_query_serial_cb,
ril_devinfo_cbd_new(di, cb, data),
ril_devinfo_cbd_free);
DBG_(di, "");
ril_devinfo_query(di, DEVINFO_QUERY_SERIAL,
ril_devinfo_query_serial_cb, cb, data);
}
static gboolean ril_devinfo_register(gpointer user_data)
static void ril_devinfo_query_svn(struct ofono_devinfo *info,
ofono_devinfo_query_cb_t cb,
void *data)
{
struct ril_devinfo *di = ril_devinfo_get_data(info);
DBG_(di, "");
ril_devinfo_query(di, DEVINFO_QUERY_SVN,
ril_devinfo_query_svn_cb, cb, data);
}
static void ril_devinfo_register(gpointer user_data)
{
struct ril_devinfo *di = user_data;
DBG("");
di->register_id = 0;
DBG_(di, "");
ofono_devinfo_register(di->info);
/* This makes the timeout a single-shot */
return FALSE;
}
static int ril_devinfo_probe(struct ofono_devinfo *info, unsigned int vendor,
@@ -142,13 +173,18 @@ static int ril_devinfo_probe(struct ofono_devinfo *info, unsigned int vendor,
struct ril_modem *modem = data;
struct ril_devinfo *di = g_new0(struct ril_devinfo, 1);
DBG("%s %s %p", ril_modem_get_path(modem), modem->imei, di);
di->log_prefix = (modem->log_prefix && modem->log_prefix[0]) ?
g_strconcat(modem->log_prefix, " ", NULL) : g_strdup("");
DBG_(di, "%s", modem->imei);
GASSERT(modem->imei);
di->q = grilio_queue_new(ril_modem_io(modem));
di->info = info;
di->imeisv = g_strdup(modem->imeisv);
di->imei = g_strdup(modem->imei);
di->register_id = g_idle_add(ril_devinfo_register, di);
di->iq = gutil_idle_queue_new();
gutil_idle_queue_add(di->iq, ril_devinfo_register, di);
ofono_devinfo_set_data(info, di);
return 0;
}
@@ -157,19 +193,14 @@ static void ril_devinfo_remove(struct ofono_devinfo *info)
{
struct ril_devinfo *di = ril_devinfo_get_data(info);
DBG("%p", di);
DBG_(di, "");
ofono_devinfo_set_data(info, NULL);
if (di->register_id > 0) {
g_source_remove(di->register_id);
}
if (di->imei_id > 0) {
g_source_remove(di->imei_id);
}
gutil_idle_queue_cancel_all(di->iq);
gutil_idle_queue_unref(di->iq);
grilio_queue_cancel_all(di->q, FALSE);
grilio_queue_unref(di->q);
g_free(di->log_prefix);
g_free(di->imeisv);
g_free(di->imei);
g_free(di);
}
@@ -178,10 +209,11 @@ const struct ofono_devinfo_driver ril_devinfo_driver = {
.name = RILMODEM_DRIVER,
.probe = ril_devinfo_probe,
.remove = ril_devinfo_remove,
.query_manufacturer = ril_devinfo_query_unsupported,
/* query_revision won't be called if query_model is missing */
.query_model = ril_devinfo_query_unsupported,
.query_revision = ril_devinfo_query_revision,
.query_serial = ril_devinfo_query_serial
.query_serial = ril_devinfo_query_serial,
.query_svn = ril_devinfo_query_svn
};
/*

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2016 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -17,7 +17,6 @@
#include "ril_network.h"
#include "ril_data.h"
#include "ril_util.h"
#include "ril_mtu.h"
#include "ril_log.h"
#include <gutil_strv.h>
@@ -25,6 +24,7 @@
#include <arpa/inet.h>
#include "common.h"
#include "mtu-watch.h"
#define CTX_ID_NONE ((unsigned int)(-1))
@@ -43,7 +43,7 @@ struct ril_gprs_context {
struct ril_data *data;
guint active_ctx_cid;
gulong calls_changed_id;
struct ril_mtu_watch *mtu_watch;
struct mtu_watch *mtu_watch;
struct ril_data_call *active_call;
struct ril_gprs_context_call activate;
struct ril_gprs_context_call deactivate;
@@ -95,7 +95,7 @@ static void ril_gprs_context_free_active_call(struct ril_gprs_context *gcd)
gcd->calls_changed_id = 0;
}
if (gcd->mtu_watch) {
ril_mtu_watch_free(gcd->mtu_watch);
mtu_watch_free(gcd->mtu_watch);
gcd->mtu_watch = NULL;
}
}
@@ -107,9 +107,9 @@ static void ril_gprs_context_set_active_call(struct ril_gprs_context *gcd,
ril_data_call_free(gcd->active_call);
gcd->active_call = ril_data_call_dup(call);
if (!gcd->mtu_watch) {
gcd->mtu_watch = ril_mtu_watch_new(MAX_MTU);
gcd->mtu_watch = mtu_watch_new(MAX_MTU);
}
ril_mtu_watch_set_ifname(gcd->mtu_watch, call->ifname);
mtu_watch_set_ifname(gcd->mtu_watch, call->ifname);
} else {
ril_gprs_context_free_active_call(gcd);
}
@@ -575,7 +575,7 @@ static void ril_gprs_context_remove(struct ofono_gprs_context *gc)
ril_data_unref(gcd->data);
ril_network_unref(gcd->network);
ril_data_call_free(gcd->active_call);
ril_mtu_watch_free(gcd->mtu_watch);
mtu_watch_free(gcd->mtu_watch);
g_free(gcd);
}

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2016 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -25,6 +25,8 @@
#include "ofono.h"
#include "sailfish_watch.h"
#define MAX_PDP_CONTEXTS (2)
#define ONLINE_TIMEOUT_SECS (15) /* 20 sec is hardcoded in ofono core */
@@ -50,24 +52,20 @@ struct ril_modem_online_request {
struct ril_modem_data {
struct ril_modem modem;
struct sailfish_watch *watch;
GRilIoQueue *q;
char *log_prefix;
char *imeisv;
char *imei;
char *ecclist_file;
gboolean pre_sim_done;
gboolean allow_data;
gulong sim_imsi_event_id;
gulong imsi_event_id;
guint online_check_id;
enum ril_modem_power_state power_state;
gulong radio_state_event_id;
ril_modem_cb_t removed_cb;
void *removed_cb_data;
ril_modem_online_cb_t online_cb;
void *online_cb_data;
struct ril_modem_online_request set_online;
struct ril_modem_online_request set_offline;
};
@@ -83,11 +81,6 @@ static struct ril_modem_data *ril_modem_data_from_ofono(struct ofono_modem *o)
return md;
}
static struct ril_modem_data *ril_modem_data_from_modem(struct ril_modem *m)
{
return m ? G_CAST(m, struct ril_modem_data, modem) : NULL;
}
static void *ril_modem_get_atom_data(struct ril_modem *modem,
enum ofono_atom_type type)
{
@@ -131,24 +124,6 @@ void ril_modem_delete(struct ril_modem *md)
}
}
void ril_modem_set_removed_cb(struct ril_modem *modem, ril_modem_cb_t cb,
void *data)
{
struct ril_modem_data *md = ril_modem_data_from_modem(modem);
md->removed_cb = cb;
md->removed_cb_data = data;
}
void ril_modem_set_online_cb(struct ril_modem *modem, ril_modem_online_cb_t cb,
void *data)
{
struct ril_modem_data *md = ril_modem_data_from_modem(modem);
md->online_cb = cb;
md->online_cb_data = data;
}
static void ril_modem_online_request_ok(struct ril_modem_online_request *req)
{
if (req->timeout_id) {
@@ -233,7 +208,7 @@ static void ril_modem_schedule_online_check(struct ril_modem_data *md)
static void ril_modem_update_radio_settings(struct ril_modem_data *md)
{
struct ril_modem *m = &md->modem;
if (m->radio->state == RADIO_STATE_ON && m->sim_settings->imsi) {
if (m->radio->state == RADIO_STATE_ON && md->watch->imsi) {
/* radio-settings.c assumes that IMSI is available */
if (!ril_modem_radio_settings(m)) {
DBG_(md, "initializing radio settings interface");
@@ -261,11 +236,11 @@ static void ril_modem_radio_state_cb(struct ril_radio *radio, void *data)
ril_modem_update_online_state(md);
}
static void ril_modem_imsi_cb(struct ril_sim_settings *settings, void *data)
static void ril_modem_imsi_cb(struct sailfish_watch *watch, void *data)
{
struct ril_modem_data *md = data;
GASSERT(md->modem.sim_settings == settings);
GASSERT(md->watch == watch);
ril_modem_update_radio_settings(md);
}
@@ -277,7 +252,9 @@ static void ril_modem_pre_sim(struct ofono_modem *modem)
md->pre_sim_done = TRUE;
ofono_devinfo_create(modem, 0, RILMODEM_DRIVER, md);
ofono_sim_create(modem, 0, RILMODEM_DRIVER, md);
ofono_voicecall_create(modem, 0, RILMODEM_DRIVER, md);
if (md->modem.config.enable_voicecall) {
ofono_voicecall_create(modem, 0, RILMODEM_DRIVER, md);
}
if (!md->radio_state_event_id) {
md->radio_state_event_id =
ril_radio_add_state_changed_handler(md->modem.radio,
@@ -311,6 +288,7 @@ static void ril_modem_post_sim(struct ofono_modem *modem)
ofono_call_forwarding_create(modem, 0, RILMODEM_DRIVER, md);
ofono_call_barring_create(modem, 0, RILMODEM_DRIVER, md);
ofono_stk_create(modem, 0, RILMODEM_DRIVER, md);
ofono_cbs_create(modem, 0, RILMODEM_DRIVER, md);
ofono_message_waiting_register(ofono_message_waiting_create(modem));
}
@@ -335,10 +313,6 @@ static void ril_modem_set_online(struct ofono_modem *modem, ofono_bool_t online,
DBG("%s going %sline", ofono_modem_get_path(modem),
online ? "on" : "off");
if (md->online_cb) {
md->online_cb(&md->modem, online, md->online_cb_data);
}
if (online) {
ril_radio_power_on(md->modem.radio, RADIO_POWER_TAG(md));
req = &md->set_online;
@@ -392,25 +366,16 @@ static void ril_modem_remove(struct ofono_modem *ofono)
struct ril_modem *modem = &md->modem;
DBG("%s", ril_modem_get_path(modem));
if (md->removed_cb) {
ril_modem_cb_t cb = md->removed_cb;
void *data = md->removed_cb_data;
md->removed_cb = NULL;
md->removed_cb_data = NULL;
cb(modem, data);
}
ofono_modem_set_data(ofono, NULL);
ril_radio_remove_handler(modem->radio, md->radio_state_event_id);
ril_radio_power_off(modem->radio, RADIO_POWER_TAG(md));
ril_radio_unref(modem->radio);
ril_sim_settings_remove_handler(modem->sim_settings,
md->sim_imsi_event_id);
ril_sim_settings_unref(modem->sim_settings);
sailfish_watch_remove_handler(md->watch, md->imsi_event_id);
sailfish_watch_unref(md->watch);
if (md->online_check_id) {
g_source_remove(md->online_check_id);
}
@@ -432,18 +397,21 @@ static void ril_modem_remove(struct ofono_modem *ofono)
grilio_queue_unref(md->q);
g_free(md->ecclist_file);
g_free(md->log_prefix);
g_free(md->imeisv);
g_free(md->imei);
g_free(md);
}
struct ril_modem *ril_modem_create(GRilIoChannel *io, const char *log_prefix,
const struct ril_slot_info *slot, struct ril_radio *radio,
struct ril_network *network, struct ril_sim_card *card,
struct ril_data *data, struct ril_sim_settings *settings,
const char *path, const char *imei, const char *imeisv,
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_cell_info *cell_info)
{
/* Skip the slash from the path, it looks like "/ril_0" */
struct ofono_modem *ofono = ofono_modem_create(slot->path + 1,
struct ofono_modem *ofono = ofono_modem_create(path + 1,
RILMODEM_DRIVER);
if (ofono) {
int err;
@@ -454,14 +422,14 @@ struct ril_modem *ril_modem_create(GRilIoChannel *io, const char *log_prefix,
* ril_plugin.c must wait until IMEI becomes known before
* creating the modem
*/
GASSERT(slot->imei);
GASSERT(imei);
/* Copy config */
modem->config = *slot->config;
modem->imei = md->imei = g_strdup(slot->imei);
modem->log_prefix = log_prefix;
modem->ecclist_file =
md->ecclist_file = g_strdup(slot->ecclist_file);
modem->config = *config;
modem->imei = md->imei = g_strdup(imei);
modem->imeisv = md->imeisv = g_strdup(imeisv);
modem->log_prefix = log_prefix; /* No need to strdup */
modem->ecclist_file = ecclist_file; /* No need to strdup */
md->log_prefix = (log_prefix && log_prefix[0]) ?
g_strconcat(log_prefix, " ", NULL) : g_strdup("");
@@ -474,14 +442,10 @@ struct ril_modem *ril_modem_create(GRilIoChannel *io, const char *log_prefix,
modem->data = ril_data_ref(data);
modem->io = grilio_channel_ref(io);
md->q = grilio_queue_new(io);
md->watch = sailfish_watch_new(path);
/*
* modem->sim_settings->imsi follows IMSI known to the ofono
* core, unlike ril_sim_info->imsi which may point to the
* cached IMSI even before the PIN code is entered.
*/
md->sim_imsi_event_id =
ril_sim_settings_add_imsi_changed_handler(settings,
md->imsi_event_id =
sailfish_watch_add_imsi_changed_handler(md->watch,
ril_modem_imsi_cb, md);
md->set_online.md = md;

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2016 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -20,6 +20,8 @@
#include "ril_util.h"
#include "ril_log.h"
#include "sailfish_watch.h"
#include <grilio_queue.h>
#include <grilio_request.h>
#include <grilio_parser.h>
@@ -41,18 +43,30 @@ enum ril_network_timer {
TIMER_COUNT
};
enum ril_network_watch_events {
WATCH_EVENT_ONLINE,
WATCH_EVENT_COUNT
};
enum ril_network_radio_event {
RADIO_EVENT_STATE_CHANGED,
RADIO_EVENT_ONLINE_CHANGED,
RADIO_EVENT_COUNT
};
enum ril_network_unsol_event {
UNSOL_EVENT_NETWORK_STATE,
UNSOL_EVENT_RADIO_CAPABILITY,
UNSOL_EVENT_COUNT
};
struct ril_network_priv {
GRilIoChannel *io;
GRilIoQueue *q;
struct ril_radio *radio;
struct ril_sim_card *sim_card;
enum ofono_radio_access_mode max_pref_mode;
struct sailfish_watch *watch;
gulong watch_event_id[WATCH_EVENT_COUNT];
int rat;
char *log_prefix;
guint operator_poll_id;
@@ -61,11 +75,12 @@ struct ril_network_priv {
guint timer[TIMER_COUNT];
gulong query_rat_id;
gulong set_rat_id;
gulong ril_event_id;
gulong unsol_event_id[UNSOL_EVENT_COUNT];
gulong settings_event_id;
gulong sim_status_event_id;
gulong radio_event_id[RADIO_EVENT_COUNT];
struct ofono_network_operator operator;
gboolean assert_rat;
};
enum ril_network_signal {
@@ -73,13 +88,15 @@ enum ril_network_signal {
SIGNAL_VOICE_STATE_CHANGED,
SIGNAL_DATA_STATE_CHANGED,
SIGNAL_PREF_MODE_CHANGED,
SIGNAL_MAX_PREF_MODE_CHANGED,
SIGNAL_COUNT
};
#define SIGNAL_OPERATOR_CHANGED_NAME "ril-network-operator-changed"
#define SIGNAL_VOICE_STATE_CHANGED_NAME "ril-network-voice-state-changed"
#define SIGNAL_DATA_STATE_CHANGED_NAME "ril-network-data-state-changed"
#define SIGNAL_PREF_MODE_CHANGED_NAME "ril-network-pref-mode-changed"
#define SIGNAL_OPERATOR_CHANGED_NAME "ril-network-operator-changed"
#define SIGNAL_VOICE_STATE_CHANGED_NAME "ril-network-voice-state-changed"
#define SIGNAL_DATA_STATE_CHANGED_NAME "ril-network-data-state-changed"
#define SIGNAL_PREF_MODE_CHANGED_NAME "ril-network-pref-mode-changed"
#define SIGNAL_MAX_PREF_MODE_CHANGED_NAME "ril-network-max-pref-mode-changed"
static guint ril_network_signals[SIGNAL_COUNT] = { 0 };
@@ -420,13 +437,16 @@ static int ril_network_mode_to_rat(struct ril_network *self,
switch (mode) {
case OFONO_RADIO_ACCESS_MODE_ANY:
case OFONO_RADIO_ACCESS_MODE_LTE:
if (self->settings->enable_4g) {
if (self->settings->techs & OFONO_RADIO_ACCESS_MODE_LTE) {
return PREF_NET_TYPE_LTE_GSM_WCDMA;
}
/* no break */
default:
case OFONO_RADIO_ACCESS_MODE_UMTS:
return PREF_NET_TYPE_GSM_WCDMA_AUTO;
if (self->settings->techs & OFONO_RADIO_ACCESS_MODE_UMTS) {
return PREF_NET_TYPE_GSM_WCDMA_AUTO;
}
/* no break */
case OFONO_RADIO_ACCESS_MODE_GSM:
return PREF_NET_TYPE_GSM_ONLY;
}
@@ -447,7 +467,7 @@ static int ril_network_pref_mode_expected(struct ril_network *self)
* it becomes necessary.
*/
const enum ofono_radio_access_mode max_pref_mode =
(priv->radio->state == RADIO_STATE_ON) ? priv->max_pref_mode :
(priv->radio->state == RADIO_STATE_ON) ? self->max_pref_mode :
OFONO_RADIO_ACCESS_MODE_GSM;
/*
@@ -467,7 +487,7 @@ static gboolean ril_network_can_set_pref_mode(struct ril_network *self)
{
struct ril_network_priv *priv = self->priv;
return priv->radio->online && ril_sim_card_ready(priv->sim_card);
return priv->watch->online && ril_sim_card_ready(priv->sim_card);
}
static gboolean ril_network_set_rat_holdoff_cb(gpointer user_data)
@@ -486,7 +506,7 @@ static gboolean ril_network_set_rat_holdoff_cb(gpointer user_data)
* and SIM card state change callbacks will schedule a new check
* when it's appropriate.
*/
if (priv->rat != rat) {
if (priv->rat != rat || priv->assert_rat) {
if (ril_network_can_set_pref_mode(self)) {
ril_network_set_pref_mode(self, rat);
} else {
@@ -527,6 +547,9 @@ static void ril_network_set_pref_mode(struct ril_network *self, int rat)
ril_network_set_pref_mode_cb, NULL, self);
grilio_request_unref(req);
/* We have submitted the request, clear the assertion flag */
priv->assert_rat = FALSE;
/* Don't do it too often */
GASSERT(!priv->timer[TIMER_SET_RAT_HOLDOFF]);
priv->timer[TIMER_SET_RAT_HOLDOFF] =
@@ -554,8 +577,7 @@ static void ril_network_check_pref_mode(struct ril_network *self,
ril_network_stop_timer(self, TIMER_SET_RAT_HOLDOFF);
}
if (priv->rat != rat) {
/* Something isn't right, we need to fix it */
if (priv->rat != rat || priv->assert_rat) {
if (!priv->timer[TIMER_SET_RAT_HOLDOFF]) {
ril_network_set_pref_mode(self, rat);
} else {
@@ -644,17 +666,25 @@ void ril_network_set_max_pref_mode(struct ril_network *self,
enum ofono_radio_access_mode max_mode,
gboolean force_check)
{
if (G_LIKELY(self)) {
struct ril_network_priv *priv = self->priv;
if (priv->max_pref_mode != max_mode || force_check) {
if (self && (self->max_pref_mode != max_mode || force_check)) {
if (self->max_pref_mode != max_mode) {
DBG_(self, "rat mode %d (%s)", max_mode,
ofono_radio_access_mode_to_string(max_mode));
priv->max_pref_mode = max_mode;
ril_network_check_pref_mode(self, TRUE);
self->max_pref_mode = max_mode;
ril_network_emit(self, SIGNAL_MAX_PREF_MODE_CHANGED);
}
ril_network_check_pref_mode(self, TRUE);
}
}
void ril_network_assert_pref_mode(struct ril_network *self, gboolean immediate)
{
struct ril_network_priv *priv = self->priv;
priv->assert_rat = TRUE;
ril_network_check_pref_mode(self, immediate);
}
gulong ril_network_add_operator_changed_handler(struct ril_network *self,
ril_network_cb_t cb, void *arg)
{
@@ -683,6 +713,13 @@ gulong ril_network_add_pref_mode_changed_handler(struct ril_network *self,
SIGNAL_PREF_MODE_CHANGED_NAME, G_CALLBACK(cb), arg) : 0;
}
gulong ril_network_add_max_pref_mode_changed_handler(struct ril_network *self,
ril_network_cb_t cb, void *arg)
{
return (G_LIKELY(self) && G_LIKELY(cb)) ? g_signal_connect(self,
SIGNAL_MAX_PREF_MODE_CHANGED_NAME, G_CALLBACK(cb), arg) : 0;
}
void ril_network_remove_handler(struct ril_network *self, gulong id)
{
if (G_LIKELY(self) && G_LIKELY(id)) {
@@ -695,7 +732,7 @@ void ril_network_remove_handlers(struct ril_network *self, gulong *ids, int n)
gutil_disconnect_handlers(self, ids, n);
}
static void ril_network_voice_state_changed_cb(GRilIoChannel *io, guint code,
static void ril_network_state_changed_cb(GRilIoChannel *io, guint code,
const void *data, guint len, void *user_data)
{
struct ril_network *self = RIL_NETWORK(user_data);
@@ -705,6 +742,16 @@ static void ril_network_voice_state_changed_cb(GRilIoChannel *io, guint code,
ril_network_poll_state(self);
}
static void ril_network_radio_capability_changed_cb(GRilIoChannel *io,
guint code, const void *data, guint len, void *user_data)
{
struct ril_network *self = RIL_NETWORK(user_data);
DBG_(self, "");
GASSERT(code == RIL_UNSOL_RADIO_CAPABILITY);
ril_network_assert_pref_mode(self, FALSE);
}
static void ril_network_radio_state_cb(struct ril_radio *radio, void *data)
{
struct ril_network *self = RIL_NETWORK(data);
@@ -715,7 +762,7 @@ static void ril_network_radio_state_cb(struct ril_radio *radio, void *data)
}
}
static void ril_network_radio_online_cb(struct ril_radio *radio, void *data)
static void ril_network_online_cb(struct sailfish_watch *watch, void *data)
{
struct ril_network *self = RIL_NETWORK(data);
@@ -767,8 +814,9 @@ static void ril_network_sim_status_changed_cb(struct ril_sim_card *sc,
}
}
struct ril_network *ril_network_new(GRilIoChannel *io, const char *log_prefix,
struct ril_radio *radio, struct ril_sim_card *sim_card,
struct ril_network *ril_network_new(const char *path, GRilIoChannel *io,
const char *log_prefix, struct ril_radio *radio,
struct ril_sim_card *sim_card,
struct ril_sim_settings *settings)
{
struct ril_network *self = g_object_new(RIL_NETWORK_TYPE, NULL);
@@ -777,20 +825,26 @@ struct ril_network *ril_network_new(GRilIoChannel *io, const char *log_prefix,
self->settings = ril_sim_settings_ref(settings);
priv->io = grilio_channel_ref(io);
priv->q = grilio_queue_new(priv->io);
priv->watch = sailfish_watch_new(path);
priv->radio = ril_radio_ref(radio);
priv->sim_card = ril_sim_card_ref(sim_card);
priv->log_prefix = (log_prefix && log_prefix[0]) ?
g_strconcat(log_prefix, " ", NULL) : g_strdup("");
DBG_(self, "");
priv->ril_event_id = grilio_channel_add_unsol_event_handler(priv->io,
ril_network_voice_state_changed_cb,
priv->unsol_event_id[UNSOL_EVENT_NETWORK_STATE] =
grilio_channel_add_unsol_event_handler(priv->io,
ril_network_state_changed_cb,
RIL_UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED, self);
priv->unsol_event_id[UNSOL_EVENT_RADIO_CAPABILITY] =
grilio_channel_add_unsol_event_handler(priv->io,
ril_network_radio_capability_changed_cb,
RIL_UNSOL_RADIO_CAPABILITY, self);
priv->radio_event_id[RADIO_EVENT_STATE_CHANGED] =
ril_radio_add_state_changed_handler(priv->radio,
ril_network_radio_state_cb, self);
priv->radio_event_id[RADIO_EVENT_ONLINE_CHANGED] =
ril_radio_add_online_changed_handler(priv->radio,
ril_network_radio_online_cb, self);
priv->watch_event_id[WATCH_EVENT_ONLINE] =
sailfish_watch_add_modem_changed_handler(priv->watch,
ril_network_online_cb, self);
priv->settings_event_id =
ril_sim_settings_add_pref_mode_changed_handler(settings,
ril_network_pref_mode_changed_cb, self);
@@ -840,57 +894,46 @@ static void ril_network_init(struct ril_network *self)
priv->rat = -1;
}
static void ril_network_dispose(GObject *object)
static void ril_network_finalize(GObject *object)
{
struct ril_network *self = RIL_NETWORK(object);
struct ril_network_priv *priv = self->priv;
enum ril_network_timer tid;
grilio_channel_remove_handlers(priv->io, &priv->ril_event_id, 1);
ril_radio_remove_handlers(priv->radio, priv->radio_event_id,
G_N_ELEMENTS(priv->radio_event_id));
ril_sim_settings_remove_handlers(self->settings,
&priv->settings_event_id, 1);
ril_sim_card_remove_handlers(priv->sim_card,
&priv->sim_status_event_id, 1);
DBG_(self, "");
for (tid=0; tid<TIMER_COUNT; tid++) {
ril_network_stop_timer(self, tid);
}
grilio_queue_cancel_all(priv->q, FALSE);
priv->set_rat_id = 0;
priv->query_rat_id = 0;
grilio_channel_remove_handlers(priv->io, priv->unsol_event_id,
G_N_ELEMENTS(priv->unsol_event_id));
G_OBJECT_CLASS(ril_network_parent_class)->dispose(object);
}
static void ril_network_finalize(GObject *object)
{
struct ril_network *self = RIL_NETWORK(object);
struct ril_network_priv *priv = self->priv;
DBG_(self, "");
g_free(priv->log_prefix);
grilio_channel_unref(priv->io);
grilio_queue_unref(priv->q);
sailfish_watch_remove_all_handlers(priv->watch, priv->watch_event_id);
sailfish_watch_unref(priv->watch);
ril_radio_remove_all_handlers(priv->radio, priv->radio_event_id);
ril_radio_unref(priv->radio);
ril_sim_card_remove_handler(priv->sim_card,
priv->sim_status_event_id);
ril_sim_card_unref(priv->sim_card);
ril_sim_settings_remove_handler(self->settings,
priv->settings_event_id);
ril_sim_settings_unref(self->settings);
g_free(priv->log_prefix);
G_OBJECT_CLASS(ril_network_parent_class)->finalize(object);
}
static void ril_network_class_init(RilNetworkClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->dispose = ril_network_dispose;
object_class->finalize = ril_network_finalize;
G_OBJECT_CLASS(klass)->finalize = ril_network_finalize;
g_type_class_add_private(klass, sizeof(struct ril_network_priv));
RIL_NETWORK_SIGNAL(klass, OPERATOR);
RIL_NETWORK_SIGNAL(klass, VOICE_STATE);
RIL_NETWORK_SIGNAL(klass, DATA_STATE);
RIL_NETWORK_SIGNAL(klass, PREF_MODE);
RIL_NETWORK_SIGNAL(klass, MAX_PREF_MODE);
}
/*

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2016 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -18,8 +18,6 @@
#include "ril_types.h"
#include <ofono/radio-settings.h>
struct ofono_network_operator;
struct ril_registration_state {
@@ -38,14 +36,16 @@ struct ril_network {
struct ril_registration_state data;
const struct ofono_network_operator *operator;
enum ofono_radio_access_mode pref_mode;
enum ofono_radio_access_mode max_pref_mode;
struct ril_sim_settings *settings;
};
struct ofono_sim;
typedef void (*ril_network_cb_t)(struct ril_network *net, void *arg);
struct ril_network *ril_network_new(GRilIoChannel *io, const char *log_prefix,
struct ril_radio *radio, struct ril_sim_card *sim_card,
struct ril_network *ril_network_new(const char *path, GRilIoChannel *io,
const char *log_prefix, struct ril_radio *radio,
struct ril_sim_card *sim_card,
struct ril_sim_settings *settings);
struct ril_network *ril_network_ref(struct ril_network *net);
void ril_network_unref(struct ril_network *net);
@@ -53,6 +53,7 @@ void ril_network_unref(struct ril_network *net);
void ril_network_set_max_pref_mode(struct ril_network *net,
enum ofono_radio_access_mode max_pref_mode,
gboolean force_check);
void ril_network_assert_pref_mode(struct ril_network *net, gboolean immediate);
gulong ril_network_add_operator_changed_handler(struct ril_network *net,
ril_network_cb_t cb, void *arg);
gulong ril_network_add_voice_state_changed_handler(struct ril_network *net,
@@ -61,6 +62,8 @@ gulong ril_network_add_data_state_changed_handler(struct ril_network *net,
ril_network_cb_t cb, void *arg);
gulong ril_network_add_pref_mode_changed_handler(struct ril_network *net,
ril_network_cb_t cb, void *arg);
gulong ril_network_add_max_pref_mode_changed_handler(struct ril_network *net,
ril_network_cb_t cb, void *arg);
void ril_network_remove_handler(struct ril_network *net, gulong id);
void ril_network_remove_handlers(struct ril_network *net, gulong *ids, int n);

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2016 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -24,7 +24,6 @@
#define RIL_OEM_RAW_TIMEOUT (60*1000) /* 60 sec */
struct ril_oem_raw {
struct ril_modem *modem;
GRilIoQueue *q;
DBusConnection *conn;
char *path;
@@ -118,7 +117,6 @@ struct ril_oem_raw *ril_oem_raw_new(struct ril_modem *modem,
struct ril_oem_raw *oem = g_new0(struct ril_oem_raw, 1);
DBG("%s", ril_modem_get_path(modem));
oem->modem = modem;
oem->path = g_strdup(ril_modem_get_path(modem));
oem->conn = dbus_connection_ref(ofono_dbus_get_connection());
oem->q = grilio_queue_new(ril_modem_io(modem));
@@ -144,8 +142,6 @@ void ril_oem_raw_free(struct ril_oem_raw *oem)
DBG("%s", oem->path);
g_dbus_unregister_interface(oem->conn, oem->path,
RIL_OEM_RAW_INTERFACE);
ofono_modem_remove_interface(oem->modem->ofono,
RIL_OEM_RAW_INTERFACE);
dbus_connection_unref(oem->conn);
grilio_queue_cancel_all(oem->q, TRUE);

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2016 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -17,6 +17,7 @@
#define RIL_PLUGIN_H
#include "ril_types.h"
#include "sailfish_manager.h"
#include <ofono/modem.h>
#include <ofono/call-barring.h>
@@ -43,31 +44,10 @@
#define RILMODEM_DRIVER "ril"
typedef struct ril_slot_info const *ril_slot_info_ptr;
struct ril_slot_info {
const char *path;
const char *imei;
const char *ecclist_file;
gboolean enabled;
gboolean sim_present;
const struct ril_slot_config *config;
};
struct ril_plugin {
const char *mms_imsi;
const char *mms_path;
const char *default_voice_imsi;
const char *default_data_imsi;
const char *default_voice_path;
const char *default_data_path;
const ril_slot_info_ptr *slots;
gboolean ready;
};
struct ril_modem {
GRilIoChannel *io;
const char *imei;
const char *imeisv;
const char *log_prefix;
const char *ecclist_file;
struct ofono_modem *ofono;
@@ -80,63 +60,27 @@ struct ril_modem {
struct ril_slot_config config;
};
#define RIL_PLUGIN_SIGNAL_VOICE_IMSI (0x01)
#define RIL_PLUGIN_SIGNAL_DATA_IMSI (0x02)
#define RIL_PLUGIN_SIGNAL_VOICE_PATH (0x04)
#define RIL_PLUGIN_SIGNAL_DATA_PATH (0x08)
#define RIL_PLUGIN_SIGNAL_ENABLED_SLOTS (0x10)
#define RIL_PLUGIN_SIGNAL_MMS_IMSI (0x20)
#define RIL_PLUGIN_SIGNAL_MMS_PATH (0x40)
#define RIL_PLUGIN_SIGNAL_READY (0x80)
typedef void (*ril_modem_cb_t)(struct ril_modem *modem, void *data);
typedef void (*ril_modem_online_cb_t)(struct ril_modem *modem, gboolean online,
void *data);
void ril_plugin_set_enabled_slots(struct ril_plugin *plugin, char **slots);
gboolean ril_plugin_set_mms_imsi(struct ril_plugin *plugin, const char *imsi);
void ril_plugin_set_default_voice_imsi(struct ril_plugin *plugin,
const char *imsi);
void ril_plugin_set_default_data_imsi(struct ril_plugin *plugin,
const char *imsi);
struct ril_oem_raw;
struct ril_oem_raw *ril_oem_raw_new(struct ril_modem *md,
struct ril_oem_raw *ril_oem_raw_new(struct ril_modem *modem,
const char *log_prefix);
void ril_oem_raw_free(struct ril_oem_raw *raw);
struct ril_sim_info_dbus;
struct ril_sim_info_dbus *ril_sim_info_dbus_new(struct ril_modem *md,
struct ril_sim_info *info);
void ril_sim_info_dbus_free(struct ril_sim_info_dbus *dbus);
struct ril_cell_info_dbus;
struct ril_cell_info_dbus *ril_cell_info_dbus_new(struct ril_modem *md,
struct ril_cell_info *info);
void ril_cell_info_dbus_free(struct ril_cell_info_dbus *dbus);
struct ril_plugin_dbus;
struct ril_plugin_dbus *ril_plugin_dbus_new(struct ril_plugin *plugin);
void ril_plugin_dbus_free(struct ril_plugin_dbus *dbus);
void ril_plugin_dbus_block_imei_requests(struct ril_plugin_dbus *dbus,
gboolean clock);
void ril_plugin_dbus_signal(struct ril_plugin_dbus *dbus, int mask);
void ril_plugin_dbus_signal_sim(struct ril_plugin_dbus *dbus, int index,
gboolean present);
struct ril_modem *ril_modem_create(GRilIoChannel *io, const char *log_prefix,
const struct ril_slot_info *slot, struct ril_radio *radio,
struct ril_network *network, struct ril_sim_card *card,
struct ril_data *data, struct ril_sim_settings *settings,
const char *path, const char *imei, const char *imeisv,
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_cell_info *cell_info);
void ril_modem_delete(struct ril_modem *modem);
struct ofono_sim *ril_modem_ofono_sim(struct ril_modem *modem);
struct ofono_gprs *ril_modem_ofono_gprs(struct ril_modem *modem);
struct ofono_netreg *ril_modem_ofono_netreg(struct ril_modem *modem);
void ril_modem_set_removed_cb(struct ril_modem *modem, ril_modem_cb_t cb,
void *data);
void ril_modem_set_online_cb(struct ril_modem *modem, ril_modem_online_cb_t cb,
void *data);
#define ril_modem_get_path(modem) ofono_modem_get_path((modem)->ofono)
#define ril_modem_4g_enabled(modem) ((modem)->config.enable_4g)
@@ -144,7 +88,7 @@ void ril_modem_set_online_cb(struct ril_modem *modem, ril_modem_online_cb_t cb,
#define ril_modem_io(modem) ((modem)->io)
int ril_sim_app_type(struct ofono_sim *sim);
int ril_netreg_check_if_really_roaming(struct ofono_netreg *netreg, gint status);
int ril_netreg_check_if_really_roaming(struct ofono_netreg *reg, gint status);
extern const struct ofono_call_barring_driver ril_call_barring_driver;
extern const struct ofono_call_forwarding_driver ril_call_forwarding_driver;

View File

@@ -1,851 +0,0 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2016 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
* 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 "ril_plugin.h"
#include <ofono/log.h>
#include <ofono/dbus.h>
#include <gutil_strv.h>
#include <gutil_log.h>
#include <gdbus.h>
#include "ofono.h"
typedef void (*ril_plugin_dbus_append_fn)(DBusMessageIter *it,
struct ril_plugin_dbus *dbus);
typedef gboolean (*ril_plugin_dbus_slot_select_fn)
(const struct ril_slot_info *slot);
typedef const char *(*ril_plugin_dbus_slot_string_fn)
(const struct ril_slot_info *slot);
struct ril_plugin_dbus_request {
DBusMessage *msg;
ril_plugin_dbus_append_fn fn;
};
struct ril_plugin_dbus {
struct ril_plugin *plugin;
DBusConnection *conn;
gboolean block_imei_req;
GSList *blocked_imei_req;
guint mms_watch;
};
#define RIL_DBUS_PATH "/"
#define RIL_DBUS_INTERFACE "org.nemomobile.ofono.ModemManager"
#define RIL_DBUS_INTERFACE_VERSION (5)
#define RIL_DBUS_SIGNAL_ENABLED_MODEMS_CHANGED "EnabledModemsChanged"
#define RIL_DBUS_SIGNAL_PRESENT_SIMS_CHANGED "PresentSimsChanged"
#define RIL_DBUS_SIGNAL_DEFAULT_VOICE_SIM_CHANGED "DefaultVoiceSimChanged"
#define RIL_DBUS_SIGNAL_DEFAULT_DATA_SIM_CHANGED "DefaultDataSimChanged"
#define RIL_DBUS_SIGNAL_DEFAULT_VOICE_MODEM_CHANGED "DefaultVoiceModemChanged"
#define RIL_DBUS_SIGNAL_DEFAULT_DATA_MODEM_CHANGED "DefaultDataModemChanged"
#define RIL_DBUS_SIGNAL_MMS_SIM_CHANGED "MmsSimChanged"
#define RIL_DBUS_SIGNAL_MMS_MODEM_CHANGED "MmsModemChanged"
#define RIL_DBUS_SIGNAL_READY_CHANGED "ReadyChanged"
#define RIL_DBUS_IMSI_AUTO "auto"
static gboolean ril_plugin_dbus_enabled(const struct ril_slot_info *slot)
{
return slot->enabled;
}
static gboolean ril_plugin_dbus_present(const struct ril_slot_info *slot)
{
return slot->sim_present;
}
static const char *ril_plugin_dbus_imei(const struct ril_slot_info *slot)
{
return slot->imei;
}
static void ril_plugin_dbus_append_path_array(DBusMessageIter *it,
struct ril_plugin_dbus *dbus, ril_plugin_dbus_slot_select_fn selector)
{
DBusMessageIter array;
const struct ril_slot_info *const *ptr = dbus->plugin->slots;
dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY,
DBUS_TYPE_OBJECT_PATH_AS_STRING, &array);
while (*ptr) {
const struct ril_slot_info *slot = *ptr++;
if (!selector || selector(slot)) {
const char *path = slot->path;
dbus_message_iter_append_basic(&array,
DBUS_TYPE_OBJECT_PATH, &path);
}
}
dbus_message_iter_close_container(it, &array);
}
static void ril_plugin_dbus_append_string_array(DBusMessageIter *it,
struct ril_plugin_dbus *dbus, ril_plugin_dbus_slot_string_fn fn)
{
DBusMessageIter array;
const struct ril_slot_info *const *ptr = dbus->plugin->slots;
dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY,
DBUS_TYPE_STRING_AS_STRING, &array);
while (*ptr) {
const struct ril_slot_info *slot = *ptr++;
const char *str = fn(slot);
if (!str) str = "";
dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING, &str);
}
dbus_message_iter_close_container(it, &array);
}
static void ril_plugin_dbus_append_boolean_array(DBusMessageIter *it,
struct ril_plugin_dbus *dbus, ril_plugin_dbus_slot_select_fn value)
{
DBusMessageIter array;
const struct ril_slot_info *const *ptr = dbus->plugin->slots;
dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY,
DBUS_TYPE_BOOLEAN_AS_STRING, &array);
while (*ptr) {
const struct ril_slot_info *slot = *ptr++;
dbus_bool_t b = value(slot);
dbus_message_iter_append_basic(&array, DBUS_TYPE_BOOLEAN, &b);
}
dbus_message_iter_close_container(it, &array);
}
static void ril_plugin_dbus_append_boolean(DBusMessageIter *it, dbus_bool_t b)
{
dbus_message_iter_append_basic(it, DBUS_TYPE_BOOLEAN, &b);
}
static void ril_plugin_dbus_append_string(DBusMessageIter *it, const char *str)
{
if (!str) str = "";
dbus_message_iter_append_basic(it, DBUS_TYPE_STRING, &str);
}
static void ril_plugin_dbus_append_imsi(DBusMessageIter *it, const char *imsi)
{
if (!imsi) imsi = RIL_DBUS_IMSI_AUTO;
dbus_message_iter_append_basic(it, DBUS_TYPE_STRING, &imsi);
}
static void ril_plugin_dbus_append_path(DBusMessageIter *it, const char *path)
{
if (!path) path = "";
/* It's DBUS_TYPE_STRING because DBUS_TYPE_OBJECT_PATH can't be empty */
dbus_message_iter_append_basic(it, DBUS_TYPE_STRING, &path);
}
static void ril_plugin_dbus_message_append_path_array(DBusMessage *msg,
struct ril_plugin_dbus *dbus, ril_plugin_dbus_slot_select_fn fn)
{
DBusMessageIter iter;
dbus_message_iter_init_append(msg, &iter);
ril_plugin_dbus_append_path_array(&iter, dbus, fn);
}
static void ril_plugin_dbus_signal_path_array(struct ril_plugin_dbus *dbus,
const char *name, ril_plugin_dbus_slot_select_fn fn)
{
DBusMessage *signal = dbus_message_new_signal(RIL_DBUS_PATH,
RIL_DBUS_INTERFACE, name);
ril_plugin_dbus_message_append_path_array(signal, dbus, fn);
g_dbus_send_message(dbus->conn, signal);
}
static inline void ril_plugin_dbus_signal_imsi(struct ril_plugin_dbus *dbus,
const char *name, const char *imsi)
{
if (!imsi) imsi = RIL_DBUS_IMSI_AUTO;
g_dbus_emit_signal(dbus->conn, RIL_DBUS_PATH, RIL_DBUS_INTERFACE,
name, DBUS_TYPE_STRING, &imsi, DBUS_TYPE_INVALID);
}
static inline void ril_plugin_dbus_signal_string(struct ril_plugin_dbus *dbus,
const char *name, const char *str)
{
if (!str) str = "";
g_dbus_emit_signal(dbus->conn, RIL_DBUS_PATH, RIL_DBUS_INTERFACE,
name, DBUS_TYPE_STRING, &str, DBUS_TYPE_INVALID);
}
static inline void ril_plugin_dbus_signal_boolean(struct ril_plugin_dbus *dbus,
const char *name, dbus_bool_t value)
{
g_dbus_emit_signal(dbus->conn, RIL_DBUS_PATH, RIL_DBUS_INTERFACE,
name, DBUS_TYPE_BOOLEAN, &value, DBUS_TYPE_INVALID);
}
void ril_plugin_dbus_signal(struct ril_plugin_dbus *dbus, int mask)
{
if (dbus) {
if (mask & RIL_PLUGIN_SIGNAL_VOICE_IMSI) {
ril_plugin_dbus_signal_imsi(dbus,
RIL_DBUS_SIGNAL_DEFAULT_VOICE_SIM_CHANGED,
dbus->plugin->default_voice_imsi);
}
if (mask & RIL_PLUGIN_SIGNAL_DATA_IMSI) {
ril_plugin_dbus_signal_imsi(dbus,
RIL_DBUS_SIGNAL_DEFAULT_DATA_SIM_CHANGED,
dbus->plugin->default_data_imsi);
}
if (mask & RIL_PLUGIN_SIGNAL_MMS_IMSI) {
ril_plugin_dbus_signal_string(dbus,
RIL_DBUS_SIGNAL_MMS_SIM_CHANGED,
dbus->plugin->mms_imsi);
}
if (mask & RIL_PLUGIN_SIGNAL_ENABLED_SLOTS) {
ril_plugin_dbus_signal_path_array(dbus,
RIL_DBUS_SIGNAL_ENABLED_MODEMS_CHANGED,
ril_plugin_dbus_enabled);
}
if (mask & RIL_PLUGIN_SIGNAL_VOICE_PATH) {
ril_plugin_dbus_signal_string(dbus,
RIL_DBUS_SIGNAL_DEFAULT_VOICE_MODEM_CHANGED,
dbus->plugin->default_voice_path);
}
if (mask & RIL_PLUGIN_SIGNAL_DATA_PATH) {
ril_plugin_dbus_signal_string(dbus,
RIL_DBUS_SIGNAL_DEFAULT_DATA_MODEM_CHANGED,
dbus->plugin->default_data_path);
}
if (mask & RIL_PLUGIN_SIGNAL_MMS_PATH) {
ril_plugin_dbus_signal_string(dbus,
RIL_DBUS_SIGNAL_MMS_MODEM_CHANGED,
dbus->plugin->mms_path);
}
if (mask & RIL_PLUGIN_SIGNAL_READY) {
ril_plugin_dbus_signal_boolean(dbus,
RIL_DBUS_SIGNAL_READY_CHANGED,
dbus->plugin->ready);
}
}
}
void ril_plugin_dbus_signal_sim(struct ril_plugin_dbus *dbus, int index,
gboolean present)
{
dbus_bool_t value = present;
g_dbus_emit_signal(dbus->conn, RIL_DBUS_PATH, RIL_DBUS_INTERFACE,
RIL_DBUS_SIGNAL_PRESENT_SIMS_CHANGED,
DBUS_TYPE_INT32, &index,
DBUS_TYPE_BOOLEAN, &value,
DBUS_TYPE_INVALID);
}
static DBusMessage *ril_plugin_dbus_reply_with_path_array(DBusMessage *msg,
struct ril_plugin_dbus *dbus, ril_plugin_dbus_slot_select_fn fn)
{
DBusMessage *reply = dbus_message_new_method_return(msg);
ril_plugin_dbus_message_append_path_array(reply, dbus, fn);
return reply;
}
static DBusMessage *ril_plugin_dbus_reply(DBusMessage *msg,
struct ril_plugin_dbus *dbus, ril_plugin_dbus_append_fn append)
{
DBusMessage *reply = dbus_message_new_method_return(msg);
DBusMessageIter iter;
dbus_message_iter_init_append(reply, &iter);
append(&iter, dbus);
return reply;
}
static void ril_plugin_dbus_unblock_request(gpointer data, gpointer user_data)
{
struct ril_plugin_dbus_request *req = data;
DBG("unblocking IMEI request %p", req);
__ofono_dbus_pending_reply(&req->msg, ril_plugin_dbus_reply(req->msg,
(struct ril_plugin_dbus *)user_data, req->fn));
g_free(req);
}
static void ril_plugin_dbus_cancel_request(gpointer data)
{
struct ril_plugin_dbus_request *req = data;
DBG("canceling IMEI request %p", req);
__ofono_dbus_pending_reply(&req->msg, __ofono_error_canceled(req->msg));
g_free(req);
}
void ril_plugin_dbus_block_imei_requests(struct ril_plugin_dbus *dbus,
gboolean block)
{
dbus->block_imei_req = block;
if (!block && dbus->blocked_imei_req) {
g_slist_foreach(dbus->blocked_imei_req,
ril_plugin_dbus_unblock_request, dbus);
g_slist_free(dbus->blocked_imei_req);
dbus->blocked_imei_req = NULL;
}
}
static DBusMessage *ril_plugin_dbus_imei_reply(DBusMessage *msg,
struct ril_plugin_dbus *dbus, ril_plugin_dbus_append_fn fn)
{
if (dbus->block_imei_req) {
struct ril_plugin_dbus_request *req =
g_new(struct ril_plugin_dbus_request, 1);
req->msg = dbus_message_ref(msg);
req->fn = fn;
dbus->blocked_imei_req = g_slist_append(dbus->blocked_imei_req,
req);
DBG("blocking IMEI request %p", req);
return NULL;
} else {
return ril_plugin_dbus_reply(msg, dbus, fn);
}
}
static void ril_plugin_dbus_append_version(DBusMessageIter *it,
struct ril_plugin_dbus *dbus)
{
dbus_int32_t version = RIL_DBUS_INTERFACE_VERSION;
dbus_message_iter_append_basic(it, DBUS_TYPE_INT32, &version);
}
static void ril_plugin_dbus_append_all(DBusMessageIter *it,
struct ril_plugin_dbus *dbus)
{
ril_plugin_dbus_append_version(it, dbus);
ril_plugin_dbus_append_path_array(it, dbus, NULL);
ril_plugin_dbus_append_path_array(it, dbus, ril_plugin_dbus_enabled);
ril_plugin_dbus_append_imsi(it, dbus->plugin->default_data_imsi);
ril_plugin_dbus_append_imsi(it, dbus->plugin->default_voice_imsi);
ril_plugin_dbus_append_path(it, dbus->plugin->default_data_path);
ril_plugin_dbus_append_path(it, dbus->plugin->default_voice_path);
}
static void ril_plugin_dbus_append_all2(DBusMessageIter *it,
struct ril_plugin_dbus *dbus)
{
ril_plugin_dbus_append_all(it, dbus);
ril_plugin_dbus_append_boolean_array(it, dbus, ril_plugin_dbus_present);
}
static void ril_plugin_dbus_append_all3(DBusMessageIter *it,
struct ril_plugin_dbus *dbus)
{
ril_plugin_dbus_append_all2(it, dbus);
ril_plugin_dbus_append_string_array(it, dbus, ril_plugin_dbus_imei);
}
static void ril_plugin_dbus_append_all4(DBusMessageIter *it,
struct ril_plugin_dbus *dbus)
{
ril_plugin_dbus_append_all3(it, dbus);
ril_plugin_dbus_append_string(it, dbus->plugin->mms_imsi);
ril_plugin_dbus_append_path(it, dbus->plugin->mms_path);
}
static void ril_plugin_dbus_append_all5(DBusMessageIter *it,
struct ril_plugin_dbus *dbus)
{
ril_plugin_dbus_append_all4(it, dbus);
ril_plugin_dbus_append_boolean(it, dbus->plugin->ready);
}
static DBusMessage *ril_plugin_dbus_get_all(DBusConnection *conn,
DBusMessage *msg, void *data)
{
return ril_plugin_dbus_reply(msg, (struct ril_plugin_dbus *)data,
ril_plugin_dbus_append_all);
}
static DBusMessage *ril_plugin_dbus_get_all2(DBusConnection *conn,
DBusMessage *msg, void *data)
{
return ril_plugin_dbus_reply(msg, (struct ril_plugin_dbus *)data,
ril_plugin_dbus_append_all2);
}
static DBusMessage *ril_plugin_dbus_get_all3(DBusConnection *conn,
DBusMessage *msg, void *data)
{
return ril_plugin_dbus_imei_reply(msg, (struct ril_plugin_dbus *)data,
ril_plugin_dbus_append_all3);
}
static DBusMessage *ril_plugin_dbus_get_all4(DBusConnection *conn,
DBusMessage *msg, void *data)
{
return ril_plugin_dbus_imei_reply(msg, (struct ril_plugin_dbus *)data,
ril_plugin_dbus_append_all4);
}
static DBusMessage *ril_plugin_dbus_get_all5(DBusConnection *conn,
DBusMessage *msg, void *data)
{
return ril_plugin_dbus_imei_reply(msg, (struct ril_plugin_dbus *)data,
ril_plugin_dbus_append_all5);
}
static DBusMessage *ril_plugin_dbus_get_interface_version(DBusConnection *conn,
DBusMessage *msg, void *data)
{
return ril_plugin_dbus_reply(msg, (struct ril_plugin_dbus *)data,
ril_plugin_dbus_append_version);
}
static DBusMessage *ril_plugin_dbus_get_available_modems(DBusConnection *conn,
DBusMessage *msg, void *data)
{
return ril_plugin_dbus_reply_with_path_array(msg,
(struct ril_plugin_dbus *)data, NULL);
}
static DBusMessage *ril_plugin_dbus_get_enabled_modems(DBusConnection *conn,
DBusMessage *msg, void *data)
{
return ril_plugin_dbus_reply_with_path_array(msg,
(struct ril_plugin_dbus *)data, ril_plugin_dbus_enabled);
}
static void ril_plugin_dbus_append_present_sims(DBusMessageIter *it,
struct ril_plugin_dbus *dbus)
{
ril_plugin_dbus_append_boolean_array(it, dbus, ril_plugin_dbus_present);
}
static DBusMessage *ril_plugin_dbus_get_present_sims(DBusConnection *conn,
DBusMessage *msg, void *data)
{
return ril_plugin_dbus_reply(msg, (struct ril_plugin_dbus *)data,
ril_plugin_dbus_append_present_sims);
}
static void ril_plugin_dbus_append_imei_array(DBusMessageIter *it,
struct ril_plugin_dbus *dbus)
{
ril_plugin_dbus_append_string_array(it, dbus, ril_plugin_dbus_imei);
}
static DBusMessage *ril_plugin_dbus_get_imei(DBusConnection *conn,
DBusMessage *msg, void *data)
{
return ril_plugin_dbus_imei_reply(msg, (struct ril_plugin_dbus *)data,
ril_plugin_dbus_append_imei_array);
}
static DBusMessage *ril_plugin_dbus_reply_with_string(DBusMessage *msg,
const char *str)
{
DBusMessage *reply = dbus_message_new_method_return(msg);
DBusMessageIter iter;
dbus_message_iter_init_append(reply, &iter);
ril_plugin_dbus_append_string(&iter, str);
return reply;
}
static DBusMessage *ril_plugin_dbus_reply_with_imsi(DBusMessage *msg,
const char *imsi)
{
DBusMessage *reply = dbus_message_new_method_return(msg);
DBusMessageIter iter;
dbus_message_iter_init_append(reply, &iter);
ril_plugin_dbus_append_imsi(&iter, imsi);
return reply;
}
static DBusMessage *ril_plugin_dbus_get_default_data_sim(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ril_plugin_dbus *dbus = data;
return ril_plugin_dbus_reply_with_imsi(msg,
dbus->plugin->default_data_imsi);
}
static DBusMessage *ril_plugin_dbus_get_default_voice_sim(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ril_plugin_dbus *dbus = data;
return ril_plugin_dbus_reply_with_imsi(msg,
dbus->plugin->default_voice_imsi);
}
static DBusMessage *ril_plugin_dbus_get_mms_sim(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ril_plugin_dbus *dbus = data;
return ril_plugin_dbus_reply_with_string(msg, dbus->plugin->mms_imsi);
}
static DBusMessage *ril_plugin_dbus_reply_with_path(DBusMessage *msg,
const char *path)
{
DBusMessage *reply = dbus_message_new_method_return(msg);
DBusMessageIter iter;
dbus_message_iter_init_append(reply, &iter);
ril_plugin_dbus_append_path(&iter, path);
return reply;
}
static DBusMessage *ril_plugin_dbus_get_default_data_modem(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ril_plugin_dbus *dbus = data;
return ril_plugin_dbus_reply_with_path(msg,
dbus->plugin->default_data_path);
}
static DBusMessage *ril_plugin_dbus_get_default_voice_modem(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ril_plugin_dbus *dbus = data;
return ril_plugin_dbus_reply_with_path(msg,
dbus->plugin->default_voice_path);
}
static DBusMessage *ril_plugin_dbus_get_mms_modem(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ril_plugin_dbus *dbus = data;
return ril_plugin_dbus_reply_with_path(msg, dbus->plugin->mms_path);
}
static DBusMessage *ril_plugin_dbus_get_ready(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ril_plugin_dbus *dbus = data;
DBusMessage *reply = dbus_message_new_method_return(msg);
DBusMessageIter it;
dbus_message_iter_init_append(reply, &it);
ril_plugin_dbus_append_boolean(&it, dbus->plugin->ready);
return reply;
}
static DBusMessage *ril_plugin_dbus_set_enabled_modems(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ril_plugin_dbus *dbus = data;
DBusMessageIter iter;
dbus_message_iter_init(msg, &iter);
if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) {
char **paths = NULL;
DBusMessageIter array;
dbus_message_iter_recurse(&iter, &array);
while (dbus_message_iter_get_arg_type(&array) ==
DBUS_TYPE_OBJECT_PATH) {
DBusBasicValue value;
dbus_message_iter_get_basic(&array, &value);
paths = gutil_strv_add(paths, value.str);
dbus_message_iter_next(&array);
}
ril_plugin_set_enabled_slots(dbus->plugin, paths);
g_strfreev(paths);
return dbus_message_new_method_return(msg);
} else {
return __ofono_error_invalid_args(msg);
}
}
static DBusMessage *ril_plugin_dbus_set_imsi(struct ril_plugin_dbus *dbus,
DBusMessage *msg, void (*apply)(struct ril_plugin *plugin,
const char *imsi))
{
DBusMessageIter iter;
dbus_message_iter_init(msg, &iter);
if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) {
DBusBasicValue value;
const char *imsi;
dbus_message_iter_get_basic(&iter, &value);
imsi = value.str;
if (!g_strcmp0(imsi, RIL_DBUS_IMSI_AUTO)) imsi = NULL;
apply(dbus->plugin, imsi);
return dbus_message_new_method_return(msg);
} else {
return __ofono_error_invalid_args(msg);
}
}
static DBusMessage *ril_plugin_dbus_set_default_voice_sim(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ril_plugin_dbus *dbus = data;
GASSERT(conn == dbus->conn);
return ril_plugin_dbus_set_imsi(dbus, msg,
ril_plugin_set_default_voice_imsi);
}
static DBusMessage *ril_plugin_dbus_set_default_data_sim(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ril_plugin_dbus *dbus = data;
GASSERT(conn == dbus->conn);
return ril_plugin_dbus_set_imsi(dbus, msg,
ril_plugin_set_default_data_imsi);
}
static void ril_plugin_dbus_mms_disconnect(DBusConnection *conn, void *data)
{
struct ril_plugin_dbus *dbus = data;
dbus->mms_watch = 0;
if (dbus->plugin->mms_imsi) {
DBG("MMS client is gone");
ril_plugin_set_mms_imsi(dbus->plugin, NULL);
}
}
static DBusMessage *ril_plugin_dbus_set_mms_sim(DBusConnection *conn,
DBusMessage *msg, void *data)
{
DBusMessageIter iter;
struct ril_plugin_dbus *dbus = data;
GASSERT(conn == dbus->conn);
dbus_message_iter_init(msg, &iter);
if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING) {
DBusBasicValue value;
const char *imsi;
dbus_message_iter_get_basic(&iter, &value);
imsi = value.str;
/*
* MMS IMSI is not persistent and has to be eventually
* reset by the client or cleaned up if the client
* unexpectedly disappears.
*/
if (ril_plugin_set_mms_imsi(dbus->plugin, imsi)) {
/*
* Clear the previous MMS owner
*/
if (dbus->mms_watch) {
g_dbus_remove_watch(dbus->conn, dbus->mms_watch);
dbus->mms_watch = 0;
}
if (dbus->plugin->mms_imsi &&
dbus->plugin->mms_imsi[0]) {
/*
* This client becomes the owner
*/
DBG("Owner: %s", dbus_message_get_sender(msg));
dbus->mms_watch =
g_dbus_add_disconnect_watch(dbus->conn,
dbus_message_get_sender(msg),
ril_plugin_dbus_mms_disconnect,
dbus, NULL);
}
return ril_plugin_dbus_reply_with_string(msg,
dbus->plugin->mms_path);
} else {
return __ofono_error_not_available(msg);
}
} else {
return __ofono_error_invalid_args(msg);
}
}
/*
* The client can call GetInterfaceVersion followed by the appropriate
* GetAllx call to get all settings in two steps. Alternatively, it can
* call GetAll followed by GetAllx based on the interface version returned
* by GetAll. In either case, two D-Bus calls are required, unless the
* client is willing to make the assumption about the ofono version it's
* talking to.
*/
#define RIL_DBUS_GET_ALL_ARGS \
{"version", "i" }, \
{"availableModems", "ao" }, \
{"enabledModems", "ao" }, \
{"defaultDataSim", "s" }, \
{"defaultVoiceSim", "s" }, \
{"defaultDataModem", "s" }, \
{"defaultVoiceModem" , "s"}
#define RIL_DBUS_GET_ALL2_ARGS \
RIL_DBUS_GET_ALL_ARGS, \
{"presentSims" , "ab"}
#define RIL_DBUS_GET_ALL3_ARGS \
RIL_DBUS_GET_ALL2_ARGS, \
{"imei" , "as"}
#define RIL_DBUS_GET_ALL4_ARGS \
RIL_DBUS_GET_ALL3_ARGS, \
{"mmsSim", "s" }, \
{"mmsModem" , "s"}
#define RIL_DBUS_GET_ALL5_ARGS \
RIL_DBUS_GET_ALL4_ARGS, \
{"ready" , "b"}
static const GDBusMethodTable ril_plugin_dbus_methods[] = {
{ GDBUS_METHOD("GetAll",
NULL, GDBUS_ARGS(RIL_DBUS_GET_ALL_ARGS),
ril_plugin_dbus_get_all) },
{ GDBUS_METHOD("GetAll2",
NULL, GDBUS_ARGS(RIL_DBUS_GET_ALL2_ARGS),
ril_plugin_dbus_get_all2) },
{ GDBUS_ASYNC_METHOD("GetAll3",
NULL, GDBUS_ARGS(RIL_DBUS_GET_ALL3_ARGS),
ril_plugin_dbus_get_all3) },
{ GDBUS_ASYNC_METHOD("GetAll4",
NULL, GDBUS_ARGS(RIL_DBUS_GET_ALL4_ARGS),
ril_plugin_dbus_get_all4) },
{ GDBUS_ASYNC_METHOD("GetAll5",
NULL, GDBUS_ARGS(RIL_DBUS_GET_ALL5_ARGS),
ril_plugin_dbus_get_all5) },
{ GDBUS_METHOD("GetInterfaceVersion",
NULL, GDBUS_ARGS({ "version", "i" }),
ril_plugin_dbus_get_interface_version) },
{ GDBUS_METHOD("GetAvailableModems",
NULL, GDBUS_ARGS({ "modems", "ao" }),
ril_plugin_dbus_get_available_modems) },
{ GDBUS_METHOD("GetEnabledModems",
NULL, GDBUS_ARGS({ "modems", "ao" }),
ril_plugin_dbus_get_enabled_modems) },
{ GDBUS_METHOD("GetPresentSims",
NULL, GDBUS_ARGS({ "presentSims", "ab" }),
ril_plugin_dbus_get_present_sims) },
{ GDBUS_ASYNC_METHOD("GetIMEI",
NULL, GDBUS_ARGS({ "imei", "as" }),
ril_plugin_dbus_get_imei) },
{ GDBUS_METHOD("GetDefaultDataSim",
NULL, GDBUS_ARGS({ "imsi", "s" }),
ril_plugin_dbus_get_default_data_sim) },
{ GDBUS_METHOD("GetDefaultVoiceSim",
NULL, GDBUS_ARGS({ "imsi", "s" }),
ril_plugin_dbus_get_default_voice_sim) },
{ GDBUS_METHOD("GetMmsSim",
NULL, GDBUS_ARGS({ "imsi", "s" }),
ril_plugin_dbus_get_mms_sim) },
{ GDBUS_METHOD("GetDefaultDataModem",
NULL, GDBUS_ARGS({ "path", "s" }),
ril_plugin_dbus_get_default_data_modem) },
{ GDBUS_METHOD("GetDefaultVoiceModem",
NULL, GDBUS_ARGS({ "path", "s" }),
ril_plugin_dbus_get_default_voice_modem) },
{ GDBUS_METHOD("GetMmsModem",
NULL, GDBUS_ARGS({ "path", "s" }),
ril_plugin_dbus_get_mms_modem) },
{ GDBUS_METHOD("GetReady",
NULL, GDBUS_ARGS({ "ready", "b" }),
ril_plugin_dbus_get_ready) },
{ GDBUS_METHOD("SetEnabledModems",
GDBUS_ARGS({ "modems", "ao" }), NULL,
ril_plugin_dbus_set_enabled_modems) },
{ GDBUS_METHOD("SetDefaultDataSim",
GDBUS_ARGS({ "imsi", "s" }), NULL,
ril_plugin_dbus_set_default_data_sim) },
{ GDBUS_METHOD("SetDefaultVoiceSim",
GDBUS_ARGS({ "imsi", "s" }), NULL,
ril_plugin_dbus_set_default_voice_sim) },
{ GDBUS_METHOD("SetMmsSim",
GDBUS_ARGS({ "imsi", "s" }), NULL,
ril_plugin_dbus_set_mms_sim) },
{ }
};
static const GDBusSignalTable ril_plugin_dbus_signals[] = {
{ GDBUS_SIGNAL(RIL_DBUS_SIGNAL_ENABLED_MODEMS_CHANGED,
GDBUS_ARGS({ "modems", "ao" })) },
{ GDBUS_SIGNAL(RIL_DBUS_SIGNAL_PRESENT_SIMS_CHANGED,
GDBUS_ARGS({"index", "i" },
{"present" , "b"})) },
{ GDBUS_SIGNAL(RIL_DBUS_SIGNAL_DEFAULT_DATA_SIM_CHANGED,
GDBUS_ARGS({ "imsi", "s" })) },
{ GDBUS_SIGNAL(RIL_DBUS_SIGNAL_DEFAULT_VOICE_SIM_CHANGED,
GDBUS_ARGS({ "imsi", "s" })) },
{ GDBUS_SIGNAL(RIL_DBUS_SIGNAL_DEFAULT_DATA_MODEM_CHANGED,
GDBUS_ARGS({ "path", "s" })) },
{ GDBUS_SIGNAL(RIL_DBUS_SIGNAL_DEFAULT_VOICE_MODEM_CHANGED,
GDBUS_ARGS({ "path", "s" })) },
{ GDBUS_SIGNAL(RIL_DBUS_SIGNAL_MMS_SIM_CHANGED,
GDBUS_ARGS({ "imsi", "s" })) },
{ GDBUS_SIGNAL(RIL_DBUS_SIGNAL_MMS_MODEM_CHANGED,
GDBUS_ARGS({ "path", "s" })) },
{ GDBUS_SIGNAL(RIL_DBUS_SIGNAL_READY_CHANGED,
GDBUS_ARGS({ "ready", "b" })) },
{ }
};
struct ril_plugin_dbus *ril_plugin_dbus_new(struct ril_plugin *plugin)
{
struct ril_plugin_dbus *dbus = g_new0(struct ril_plugin_dbus, 1);
dbus->conn = dbus_connection_ref(ofono_dbus_get_connection());
dbus->plugin = plugin;
if (g_dbus_register_interface(dbus->conn, RIL_DBUS_PATH,
RIL_DBUS_INTERFACE, ril_plugin_dbus_methods,
ril_plugin_dbus_signals, NULL, dbus, NULL)) {
return dbus;
} else {
ofono_error("RIL D-Bus register failed");
ril_plugin_dbus_free(dbus);
return NULL;
}
}
void ril_plugin_dbus_free(struct ril_plugin_dbus *dbus)
{
if (dbus) {
if (dbus->mms_watch) {
g_dbus_remove_watch(dbus->conn, dbus->mms_watch);
}
g_slist_free_full(dbus->blocked_imei_req,
ril_plugin_dbus_cancel_request);
g_dbus_unregister_interface(dbus->conn, RIL_DBUS_PATH,
RIL_DBUS_INTERFACE);
dbus_connection_unref(dbus->conn);
g_free(dbus);
}
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2016 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -50,14 +50,12 @@ struct ril_radio_priv {
enum ril_radio_signal {
SIGNAL_STATE_CHANGED,
SIGNAL_ONLINE_CHANGED,
SIGNAL_COUNT
};
#define POWER_RETRY_SECS (1)
#define SIGNAL_STATE_CHANGED_NAME "ril-radio-state-changed"
#define SIGNAL_ONLINE_CHANGED_NAME "ril-radio-online-changed"
static guint ril_radio_signals[SIGNAL_COUNT] = { 0 };
@@ -77,8 +75,7 @@ static inline gboolean ril_radio_power_should_be_on(struct ril_radio *self)
{
struct ril_radio_priv *priv = self->priv;
return self->online && !priv->power_cycle &&
g_hash_table_size(priv->req_table) > 0;
return !priv->power_cycle && g_hash_table_size(priv->req_table) > 0;
}
static inline gboolean ril_radio_state_off(enum ril_radio_state radio_state)
@@ -99,13 +96,14 @@ static inline void ril_radio_emit_signal(struct ril_radio *self,
static gboolean ril_radio_power_request_retry_cb(gpointer user_data)
{
struct ril_radio *self = user_data;
struct ril_radio *self = RIL_RADIO(user_data);
struct ril_radio_priv *priv = self->priv;
DBG("%s", priv->log_prefix);
GASSERT(priv->retry_id);
priv->retry_id = 0;
ril_radio_submit_power_request(self, ril_radio_power_should_be_on(self));
ril_radio_submit_power_request(self,
ril_radio_power_should_be_on(self));
return G_SOURCE_REMOVE;
}
@@ -126,7 +124,7 @@ static void ril_radio_check_state(struct ril_radio *self)
struct ril_radio_priv *priv = self->priv;
if (!priv->pending_id) {
const gboolean should_be_on = ril_radio_power_should_be_on(self);
gboolean should_be_on = ril_radio_power_should_be_on(self);
if (ril_radio_state_on(self->priv->last_known_state) ==
should_be_on) {
@@ -157,7 +155,7 @@ static void ril_radio_check_state(struct ril_radio *self)
static void ril_radio_power_request_cb(GRilIoChannel *channel, int ril_status,
const void *data, guint len, void *user_data)
{
struct ril_radio *self = user_data;
struct ril_radio *self = RIL_RADIO(user_data);
struct ril_radio_priv *priv = self->priv;
GASSERT(priv->pending_id);
@@ -177,11 +175,17 @@ static void ril_radio_power_request_cb(GRilIoChannel *channel, int ril_status,
static void ril_radio_submit_power_request(struct ril_radio *self, gboolean on)
{
/*
* RIL_REQUEST_RADIO_POWER
*
* "data" is int *
* ((int *)data)[0] is > 0 for "Radio On"
* ((int *)data)[0] is == 0 for "Radio Off"
*
* "response" is NULL
**/
GRilIoRequest *req = grilio_request_array_int32_new(1, on);
struct ril_radio_priv *priv = self->priv;
GRilIoRequest *req = grilio_request_sized_new(8);
grilio_request_append_int32(req, 1);
grilio_request_append_int32(req, on); /* Radio ON=1, OFF=0 */
priv->next_state_valid = FALSE;
priv->next_state = on;
@@ -189,8 +193,10 @@ static void ril_radio_submit_power_request(struct ril_radio *self, gboolean on)
ril_radio_cancel_retry(self);
GASSERT(!priv->pending_id);
grilio_request_set_blocking(req, TRUE);
priv->pending_id = grilio_queue_send_request_full(priv->q, req,
RIL_REQUEST_RADIO_POWER, ril_radio_power_request_cb, NULL, self);
RIL_REQUEST_RADIO_POWER, ril_radio_power_request_cb,
NULL, self);
grilio_request_unref(req);
}
@@ -274,19 +280,6 @@ void ril_radio_power_off(struct ril_radio *self, gpointer tag)
}
}
void ril_radio_set_online(struct ril_radio *self, gboolean online)
{
if (G_LIKELY(self) && self->online != online) {
gboolean on, was_on = ril_radio_power_should_be_on(self);
self->online = online;
on = ril_radio_power_should_be_on(self);
if (was_on != on) {
ril_radio_power_request(self, on, FALSE);
}
ril_radio_emit_signal(self, SIGNAL_ONLINE_CHANGED);
}
}
gulong ril_radio_add_state_changed_handler(struct ril_radio *self,
ril_radio_cb_t cb, void *arg)
{
@@ -294,13 +287,6 @@ gulong ril_radio_add_state_changed_handler(struct ril_radio *self,
SIGNAL_STATE_CHANGED_NAME, G_CALLBACK(cb), arg) : 0;
}
gulong ril_radio_add_online_changed_handler(struct ril_radio *self,
ril_radio_cb_t cb, void *arg)
{
return (G_LIKELY(self) && G_LIKELY(cb)) ? g_signal_connect(self,
SIGNAL_ONLINE_CHANGED_NAME, G_CALLBACK(cb), arg) : 0;
}
void ril_radio_remove_handler(struct ril_radio *self, gulong id)
{
if (G_LIKELY(self) && G_LIKELY(id)) {
@@ -330,7 +316,7 @@ enum ril_radio_state ril_radio_state_parse(const void *data, guint len)
static void ril_radio_state_changed(GRilIoChannel *io, guint code,
const void *data, guint len, void *user_data)
{
struct ril_radio *self = user_data;
struct ril_radio *self = RIL_RADIO(user_data);
enum ril_radio_state radio_state = ril_radio_state_parse(data, len);
GASSERT(code == RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED);
@@ -438,7 +424,6 @@ static void ril_radio_class_init(RilRadioClass *klass)
object_class->finalize = ril_radio_finalize;
g_type_class_add_private(klass, sizeof(struct ril_radio_priv));
NEW_SIGNAL(klass, STATE);
NEW_SIGNAL(klass, ONLINE);
}
/*

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2016 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -22,7 +22,6 @@ struct ril_radio {
GObject object;
struct ril_radio_priv *priv;
enum ril_radio_state state;
gboolean online;
};
typedef void (*ril_radio_cb_t)(struct ril_radio *radio, void *arg);
@@ -35,15 +34,15 @@ void ril_radio_power_on(struct ril_radio *radio, gpointer tag);
void ril_radio_power_off(struct ril_radio *radio, gpointer tag);
void ril_radio_power_cycle(struct ril_radio *radio);
void ril_radio_confirm_power_on(struct ril_radio *radio);
void ril_radio_set_online(struct ril_radio *radio, gboolean online);
gulong ril_radio_add_state_changed_handler(struct ril_radio *radio,
ril_radio_cb_t cb, void *arg);
gulong ril_radio_add_online_changed_handler(struct ril_radio *radio,
ril_radio_cb_t cb, void *arg);
void ril_radio_remove_handler(struct ril_radio *radio, gulong id);
void ril_radio_remove_handlers(struct ril_radio *radio, gulong *ids, int n);
enum ril_radio_state ril_radio_state_parse(const void *data, guint len);
#define ril_radio_remove_all_handlers(r,ids) \
ril_radio_remove_handlers(r, ids, G_N_ELEMENTS(ids))
#endif /* RIL_RADIO_H */
/*

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,66 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2017 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
* 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.
*/
#ifndef RIL_RADIO_CAPS_H
#define RIL_RADIO_CAPS_H
#include "ril_types.h"
struct ril_data_manager;
struct ril_radio_caps;
struct ril_radio_caps_manager;
struct ril_radio_capability;
typedef void (*ril_radio_caps_manager_cb_t)(struct ril_radio_caps_manager *mgr,
void *user_data);
/* ril_radio_capability pointer is NULL if functionality is unsupported */
typedef void (*ril_radio_caps_check_cb_t)
(const struct ril_radio_capability *cap, void *user_data);
/* The check can be cancelled with grilio_channel_cancel_request */
guint ril_radio_caps_check(GRilIoChannel *io, ril_radio_caps_check_cb_t cb,
void *user_data);
/* There should be a single ril_radio_caps_manager shared by all all modems */
struct ril_radio_caps_manager *ril_radio_caps_manager_new
(struct ril_data_manager *dm);
struct ril_radio_caps_manager *ril_radio_caps_manager_ref
(struct ril_radio_caps_manager *mgr);
void ril_radio_caps_manager_unref(struct ril_radio_caps_manager *mgr);
gulong ril_radio_caps_manager_add_aborted_handler
(struct ril_radio_caps_manager *mgr,
ril_radio_caps_manager_cb_t cb, void *arg);
void ril_radio_caps_manager_remove_handler(struct ril_radio_caps_manager *mgr,
gulong id);
/* And one ril_radio_caps object per modem */
struct ril_radio_caps *ril_radio_caps_new(struct ril_radio_caps_manager *mgr,
const char *log_prefix, GRilIoChannel *io,
struct ril_data *data, struct ril_radio *radio,
struct ril_sim_card *sim, struct ril_network *net,
const struct ril_slot_config *config,
const struct ril_radio_capability *cap);
struct ril_radio_caps *ril_radio_caps_ref(struct ril_radio_caps *caps);
void ril_radio_caps_unref(struct ril_radio_caps *caps);
#endif /* RIL_RADIO_CAPS_H */
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2016 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -17,7 +17,6 @@
#include "ril_sim_settings.h"
#include "ril_util.h"
#include "ril_log.h"
#include "ril_constants.h"
struct ril_radio_settings {
struct ofono_radio_settings *rs;
@@ -113,15 +112,11 @@ static gboolean ril_radio_settings_query_available_rats_cb(gpointer data)
struct ofono_error error;
struct ril_radio_settings_cbd *cbd = data;
struct ril_radio_settings *rsd = cbd->rsd;
guint rats = OFONO_RADIO_ACCESS_MODE_GSM | OFONO_RADIO_ACCESS_MODE_UMTS;
if (cbd->rsd->settings->enable_4g) {
rats |= OFONO_RADIO_ACCESS_MODE_LTE;
}
GASSERT(cbd->rsd->source_id);
GASSERT(rsd->source_id);
rsd->source_id = 0;
cbd->cb.available_rats(ril_error_ok(&error), rats, cbd->data);
cbd->cb.available_rats(ril_error_ok(&error), rsd->settings->techs,
cbd->data);
return G_SOURCE_REMOVE;
}
@@ -132,8 +127,8 @@ static void ril_radio_settings_query_available_rats(
struct ril_radio_settings *rsd = ril_radio_settings_get_data(rs);
DBG_(rsd, "");
ril_radio_settings_later(rsd, ril_radio_settings_query_available_rats_cb,
cb, data);
ril_radio_settings_later(rsd,
ril_radio_settings_query_available_rats_cb, cb, data);
}
static gboolean ril_radio_settings_register(gpointer user_data)

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2016 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -40,13 +40,6 @@
/* FID/path of SIM/USIM root directory */
#define ROOTMF "3F00"
/* RIL_Request* parameter counts */
#define GET_IMSI_NUM_PARAMS 1
#define ENTER_SIM_PIN_PARAMS 2
#define SET_FACILITY_LOCK_PARAMS 5
#define ENTER_SIM_PUK_PARAMS 3
#define CHANGE_SIM_PIN_PARAMS 3
/* P2 coding (modes) for READ RECORD and UPDATE RECORD (see TS 102.221) */
#define MODE_SELECTED (0x00) /* Currently selected EF */
#define MODE_CURRENT (0x04) /* P1='00' denotes the current record */
@@ -65,6 +58,13 @@
*
* The same applies to the app_type.
*/
enum ril_sim_card_event {
SIM_CARD_STATUS_EVENT,
SIM_CARD_APP_EVENT,
SIM_CARD_EVENT_COUNT
};
struct ril_sim {
GRilIoChannel *io;
GRilIoQueue *q;
@@ -76,7 +76,8 @@ struct ril_sim {
gboolean empty_pin_query_allowed;
gboolean inserted;
guint idle_id;
gulong card_status_id;
gulong card_event_id[SIM_CARD_EVENT_COUNT];
guint query_pin_retries_id;
const char *log_prefix;
char *allocated_log_prefix;
@@ -85,6 +86,7 @@ struct ril_sim {
ofono_sim_passwd_cb_t query_passwd_state_cb;
void *query_passwd_state_cb_data;
guint query_passwd_state_timeout_id;
gulong query_passwd_state_sim_status_refresh_id;
};
struct ril_sim_io_response {
@@ -100,11 +102,11 @@ struct ril_sim_cbd {
ofono_sim_read_cb_t read;
ofono_sim_write_cb_t write;
ofono_sim_imsi_cb_t imsi;
ofono_sim_pin_retries_cb_t retries;
ofono_query_facility_lock_cb_t query_facility_lock;
gpointer ptr;
} cb;
gpointer data;
guint req_id;
};
struct ril_sim_pin_cbd {
@@ -119,6 +121,49 @@ struct ril_sim_pin_cbd {
gulong card_status_id;
};
struct ril_sim_retry_query_cbd {
struct ril_sim *sd;
ofono_sim_pin_retries_cb_t cb;
void *data;
guint query_index;
};
struct ril_sim_retry_query {
const char *name;
enum ofono_sim_password_type passwd_type;
guint req_code;
GRilIoRequest *(*new_req)(struct ril_sim *sd);
};
static GRilIoRequest *ril_sim_empty_sim_pin_req(struct ril_sim *sd);
static GRilIoRequest *ril_sim_empty_sim_puk_req(struct ril_sim *sd);
static void ril_sim_query_retry_count_cb(GRilIoChannel *io, int status,
const void *data, guint len, void *user_data);
static const struct ril_sim_retry_query ril_sim_retry_query_types[] = {
{
"pin",
OFONO_SIM_PASSWORD_SIM_PIN,
RIL_REQUEST_ENTER_SIM_PIN,
ril_sim_empty_sim_pin_req
},{
"pin2",
OFONO_SIM_PASSWORD_SIM_PIN2,
RIL_REQUEST_ENTER_SIM_PIN2,
ril_sim_empty_sim_pin_req
},{
"puk",
OFONO_SIM_PASSWORD_SIM_PUK,
RIL_REQUEST_ENTER_SIM_PUK,
ril_sim_empty_sim_puk_req
},{
"puk2",
OFONO_SIM_PASSWORD_SIM_PUK2,
RIL_REQUEST_ENTER_SIM_PUK2,
ril_sim_empty_sim_puk_req
}
};
#define DBG_(sd,fmt,args...) DBG("%s" fmt, (sd)->log_prefix, ##args)
#define ril_sim_cbd_free g_free
@@ -378,21 +423,13 @@ static void ril_sim_file_info_cb(GRilIoChannel *io, int status,
struct ofono_error error;
DBG_(sd, "");
/*
* In case sim card has been removed prior to this callback has been
* called we must not call the core call back method as otherwise the
* core will crash.
*/
if (!sd->inserted) {
ofono_error("No SIM card");
return;
}
ril_sim_card_sim_io_finished(sd->card, cbd->req_id);
ril_error_init_failure(&error);
res = ril_sim_parse_io_response(data, len);
if (ril_sim_io_response_ok(res) && status == RIL_E_SUCCESS) {
if (!sd->inserted) {
DBG_(sd, "No SIM card");
} else if (ril_sim_io_response_ok(res) && status == RIL_E_SUCCESS) {
gboolean ok = FALSE;
guchar access[3] = { 0x00, 0x00, 0x00 };
guchar file_status = EF_STATUS_VALID;
@@ -447,8 +484,10 @@ static void ril_sim_request_io(struct ril_sim *sd, guint cmd, int fileid,
grilio_request_append_utf8(req, NULL); /* pin2; only for writes */
grilio_request_append_utf8(req, ril_sim_app_id(sd));
grilio_queue_send_request_full(sd->q, req, RIL_REQUEST_SIM_IO,
cb, ril_sim_cbd_free, cbd);
grilio_request_set_blocking(req, TRUE);
cbd->req_id = grilio_queue_send_request_full(sd->q, req,
RIL_REQUEST_SIM_IO, cb, ril_sim_cbd_free, cbd);
ril_sim_card_sim_io_started(sd->card, cbd->req_id);
grilio_request_unref(req);
}
@@ -470,6 +509,8 @@ static void ril_sim_read_cb(GRilIoChannel *io, int status,
struct ofono_error err;
DBG_(cbd->sd, "");
ril_sim_card_sim_io_finished(cbd->sd->card, cbd->req_id);
res = ril_sim_parse_io_response(data, len);
if (ril_sim_io_response_ok(res) && status == RIL_E_SUCCESS) {
cb(ril_error_ok(&err), res->data, res->data_len, cbd->data);
@@ -523,6 +564,8 @@ static void ril_sim_write_cb(GRilIoChannel *io, int status,
struct ofono_error err;
DBG_(cbd->sd, "");
ril_sim_card_sim_io_finished(cbd->sd->card, cbd->req_id);
res = ril_sim_parse_io_response(data, len);
if (ril_sim_io_response_ok(res) && status == RIL_E_SUCCESS) {
cb(ril_error_ok(&err), cbd->data);
@@ -583,6 +626,8 @@ static void ril_sim_get_imsi_cb(GRilIoChannel *io, int status,
ofono_sim_imsi_cb_t cb = cbd->cb.imsi;
struct ofono_error error;
ril_sim_card_sim_io_finished(cbd->sd->card, cbd->req_id);
if (status == RIL_E_SUCCESS) {
gchar *imsi;
GRilIoParser rilp;
@@ -607,11 +652,11 @@ static void ril_sim_read_imsi(struct ofono_sim *sim, ofono_sim_imsi_cb_t cb,
void *data)
{
struct ril_sim *sd = ril_sim_get_data(sim);
GRilIoRequest *req = grilio_request_sized_new(60);
const char *app_id = ril_sim_app_id(sd);
struct ril_sim_cbd *cbd = ril_sim_cbd_new(sd, cb, data);
GRilIoRequest *req = grilio_request_array_utf8_new(1, app_id);
DBG_(sd, "%s", ril_sim_app_id(sd));
grilio_request_append_int32(req, GET_IMSI_NUM_PARAMS);
grilio_request_append_utf8(req, ril_sim_app_id(sd));
DBG_(sd, "%s", app_id);
/*
* If we fail the .read_imsi call, ofono gets into "Unable to
@@ -619,9 +664,11 @@ static void ril_sim_read_imsi(struct ofono_sim *sim, ofono_sim_imsi_cb_t cb,
* on failure.
*/
grilio_request_set_retry(req, RIL_RETRY_MS, -1);
grilio_queue_send_request_full(sd->q, req, RIL_REQUEST_GET_IMSI,
ril_sim_get_imsi_cb, ril_sim_cbd_free,
ril_sim_cbd_new(sd, cb, data));
grilio_request_set_blocking(req, TRUE);
cbd->req_id = grilio_queue_send_request_full(sd->q, req,
RIL_REQUEST_GET_IMSI, ril_sim_get_imsi_cb,
ril_sim_cbd_free, cbd);
ril_sim_card_sim_io_started(sd->card, cbd->req_id);
grilio_request_unref(req);
}
@@ -700,6 +747,12 @@ static void ril_sim_finish_passwd_state_query(struct ril_sim *sd,
sd->query_passwd_state_timeout_id = 0;
}
if (sd->query_passwd_state_sim_status_refresh_id) {
ril_sim_card_remove_handler(sd->card,
sd->query_passwd_state_sim_status_refresh_id);
sd->query_passwd_state_sim_status_refresh_id = 0;
}
if (sd->query_passwd_state_cb) {
ofono_sim_passwd_cb_t cb = sd->query_passwd_state_cb;
void *data = sd->query_passwd_state_cb_data;
@@ -718,6 +771,34 @@ static void ril_sim_finish_passwd_state_query(struct ril_sim *sd,
}
}
static void ril_sim_check_perm_lock(struct ril_sim *sd)
{
struct ril_sim_card *sc = sd->card;
/*
* Zero number of retries in the PUK state indicates to the ofono
* client that the card is permanently locked. This is different
* from the case when the number of retries is negative (which
* means that PUK is required but the number of remaining attempts
* is not available).
*/
if (sc->app && sc->app->app_state == RIL_APPSTATE_PUK &&
sc->app->pin1_state == RIL_PINSTATE_ENABLED_PERM_BLOCKED) {
/*
* It makes no sense for RIL to return non-zero number of
* remaining attempts in PERM_LOCKED state. So when we get
* here, the number of retries has to be negative (unknown)
* or zero. Otherwise, something must be broken.
*/
GASSERT(sd->retries[OFONO_SIM_PASSWORD_SIM_PUK] <= 0);
if (sd->retries[OFONO_SIM_PASSWORD_SIM_PUK] < 0) {
sd->retries[OFONO_SIM_PASSWORD_SIM_PUK] = 0;
DBG_(sd, "SIM card is locked");
}
}
}
static void ril_sim_invalidate_passwd_state(struct ril_sim *sd)
{
guint i;
@@ -727,10 +808,16 @@ static void ril_sim_invalidate_passwd_state(struct ril_sim *sd)
sd->retries[i] = -1;
}
ril_sim_check_perm_lock(sd);
ril_sim_finish_passwd_state_query(sd, OFONO_SIM_PASSWORD_INVALID);
}
static void ril_sim_status_cb(struct ril_sim_card *sc, void *user_data)
static void ril_sim_app_changed_cb(struct ril_sim_card *sc, void *user_data)
{
ril_sim_check_perm_lock((struct ril_sim *)user_data);
}
static void ril_sim_status_changed_cb(struct ril_sim_card *sc, void *user_data)
{
struct ril_sim *sd = user_data;
@@ -739,6 +826,7 @@ static void ril_sim_status_cb(struct ril_sim_card *sc, void *user_data)
if (sc->app) {
enum ofono_sim_password_type ps;
ril_sim_check_perm_lock(sd);
if (!sd->inserted) {
sd->inserted = TRUE;
ofono_info("SIM card OK");
@@ -773,14 +861,28 @@ static int ril_sim_parse_retry_count(const void *data, guint len)
return retry_count;
}
static GRilIoRequest *ril_sim_enter_sim_req(struct ril_sim *sd, const char *pw)
static GRilIoRequest *ril_sim_enter_sim_pin_req(struct ril_sim *sd,
const char *pin)
{
const char *app_id = ril_sim_app_id(sd);
if (app_id) {
GRilIoRequest *req = grilio_request_new();
grilio_request_append_int32(req, ENTER_SIM_PIN_PARAMS);
grilio_request_append_utf8(req, pw);
grilio_request_append_utf8(req, app_id);
GRilIoRequest *req = grilio_request_array_utf8_new(2,
pin, app_id);
grilio_request_set_blocking(req, TRUE);
return req;
}
return NULL;
}
static GRilIoRequest *ril_sim_enter_sim_puk_req(struct ril_sim *sd,
const char *puk, const char *pin)
{
const char *app_id = ril_sim_app_id(sd);
if (app_id) {
GRilIoRequest *req = grilio_request_array_utf8_new(3,
puk, pin, app_id);
grilio_request_set_blocking(req, TRUE);
return req;
}
return NULL;
@@ -790,63 +892,90 @@ static GRilIoRequest *ril_sim_enter_sim_req(struct ril_sim *sd, const char *pw)
* Some RIL implementations allow to query the retry count
* by sending the empty pin in any state.
*/
static void ril_sim_query_pin2_retries_cb(GRilIoChannel *io, int status,
const void *data, guint len, void *user_data)
static GRilIoRequest *ril_sim_empty_sim_pin_req(struct ril_sim *sd)
{
struct ril_sim_cbd *cbd = user_data;
struct ril_sim *sd = cbd->sd;
ofono_sim_pin_retries_cb_t cb = cbd->cb.retries;
struct ofono_error error;
if (status == RIL_E_SUCCESS) {
const int retry_count = ril_sim_parse_retry_count(data, len);
DBG_(sd, "pin2 retry_count=%d", retry_count);
sd->retries[OFONO_SIM_PASSWORD_SIM_PIN2] = retry_count;
} else {
ofono_error("pin2 retry query is not supported");
sd->empty_pin_query_allowed = FALSE;
}
cb(ril_error_ok(&error), sd->retries, cbd->data);
return ril_sim_enter_sim_pin_req(sd, "");
}
static gboolean ril_sim_query_pin2_retry_count(struct ril_sim *sd,
static GRilIoRequest *ril_sim_empty_sim_puk_req(struct ril_sim *sd)
{
return ril_sim_enter_sim_puk_req(sd, "", "");
}
static struct ril_sim_retry_query_cbd *ril_sim_retry_query_cbd_new(
struct ril_sim *sd, guint query_index,
ofono_sim_pin_retries_cb_t cb, void *data)
{
if (sd->empty_pin_query_allowed &&
sd->retries[OFONO_SIM_PASSWORD_SIM_PIN2] < 0) {
GRilIoRequest *req = ril_sim_enter_sim_req(sd, "");
if (req) {
DBG_(sd, "querying pin2 retry count...");
grilio_queue_send_request_full(sd->q, req,
RIL_REQUEST_ENTER_SIM_PIN2,
ril_sim_query_pin2_retries_cb,
ril_sim_cbd_free,
ril_sim_cbd_new(sd, cb, data));
grilio_request_unref(req);
return TRUE;
}
}
return FALSE;
struct ril_sim_retry_query_cbd *cbd =
g_new(struct ril_sim_retry_query_cbd, 1);
cbd->sd = sd;
cbd->cb = cb;
cbd->data = data;
cbd->query_index = query_index;
return cbd;
}
static void ril_sim_query_pin_retries_cb(GRilIoChannel *io, int status,
static gboolean ril_sim_query_retry_count(struct ril_sim *sd,
guint start_index, ofono_sim_pin_retries_cb_t cb, void *data)
{
guint id = 0;
if (sd->empty_pin_query_allowed) {
guint i = start_index;
/* Find the first unknown retry count that we can query. */
while (i < G_N_ELEMENTS(ril_sim_retry_query_types)) {
const struct ril_sim_retry_query *query =
ril_sim_retry_query_types + i;
if (sd->retries[query->passwd_type] < 0) {
GRilIoRequest *req = query->new_req(sd);
if (req) {
DBG_(sd, "querying %s retry count...",
query->name);
id = grilio_queue_send_request_full(
sd->q, req, query->req_code,
ril_sim_query_retry_count_cb,
g_free,
ril_sim_retry_query_cbd_new(
sd, i, cb, data));
grilio_request_unref(req);
}
break;
}
i++;
}
}
return id;
}
static void ril_sim_query_retry_count_cb(GRilIoChannel *io, int status,
const void *data, guint len, void *user_data)
{
struct ril_sim_cbd *cbd = user_data;
struct ril_sim_retry_query_cbd *cbd = user_data;
struct ril_sim *sd = cbd->sd;
ofono_sim_pin_retries_cb_t cb = cbd->cb.retries;
struct ofono_error error;
GASSERT(sd->query_pin_retries_id);
sd->query_pin_retries_id = 0;
if (status == RIL_E_SUCCESS) {
const int retry_count = ril_sim_parse_retry_count(data, len);
DBG_(sd, "pin retry_count=%d", retry_count);
sd->retries[OFONO_SIM_PASSWORD_SIM_PIN] = retry_count;
if (ril_sim_query_pin2_retry_count(sd, cb, cbd->data)) {
/*
* ril_sim_query_pin2_retries_cb will invoke
* the completion callback
*/
const struct ril_sim_retry_query *query =
ril_sim_retry_query_types + cbd->query_index;
DBG_(sd, "%s retry_count=%d", query->name, retry_count);
sd->retries[query->passwd_type] = retry_count;
/* Submit the next request */
if ((sd->query_pin_retries_id =
ril_sim_query_retry_count(sd, cbd->query_index + 1,
cbd->cb, cbd->data)) != 0) {
/* The next request is pending */
return;
}
} else {
@@ -854,43 +983,32 @@ static void ril_sim_query_pin_retries_cb(GRilIoChannel *io, int status,
sd->empty_pin_query_allowed = FALSE;
}
cb(ril_error_ok(&error), sd->retries, cbd->data);
}
static gboolean ril_sim_query_pin_retry_count(struct ril_sim *sd,
ofono_sim_pin_retries_cb_t cb, void *data)
{
if (sd->empty_pin_query_allowed &&
sd->retries[OFONO_SIM_PASSWORD_SIM_PIN] < 0) {
GRilIoRequest *req = ril_sim_enter_sim_req(sd, "");
if (req) {
DBG_(sd, "querying pin retry count...");
grilio_queue_send_request_full(sd->q, req,
RIL_REQUEST_ENTER_SIM_PIN,
ril_sim_query_pin_retries_cb,
ril_sim_cbd_free,
ril_sim_cbd_new(sd, cb, data));
grilio_request_unref(req);
return TRUE;
}
}
return FALSE;
cbd->cb(ril_error_ok(&error), sd->retries, cbd->data);
}
static void ril_sim_query_pin_retries(struct ofono_sim *sim,
ofono_sim_pin_retries_cb_t cb, void *data)
{
struct ofono_error error;
struct ril_sim *sd = ril_sim_get_data(sim);
DBG_(sd, "");
if (ril_sim_query_pin_retry_count(sd, cb, data) ||
ril_sim_query_pin2_retry_count(sd, cb, data)) {
/* Wait for completion of PIN and then PIN2 query */
return;
}
grilio_queue_cancel_request(sd->q, sd->query_pin_retries_id, FALSE);
sd->query_pin_retries_id = ril_sim_query_retry_count(sd, 0, cb, data);
if (!sd->query_pin_retries_id) {
struct ofono_error error;
cb(ril_error_ok(&error), sd->retries, data);
/* Nothing to wait for */
cb(ril_error_ok(&error), sd->retries, data);
}
}
static void ril_sim_query_passwd_state_complete_cb(struct ril_sim_card *sc,
void *user_data)
{
struct ril_sim *sd = user_data;
GASSERT(sd->query_passwd_state_sim_status_refresh_id);
ril_sim_finish_passwd_state_query(sd, ril_sim_passwd_state(sd));
}
static gboolean ril_sim_query_passwd_state_timeout_cb(gpointer user_data)
@@ -908,29 +1026,41 @@ static void ril_sim_query_passwd_state(struct ofono_sim *sim,
ofono_sim_passwd_cb_t cb, void *data)
{
struct ril_sim *sd = ril_sim_get_data(sim);
enum ofono_sim_password_type passwd_state = ril_sim_passwd_state(sd);
struct ofono_error error;
if (sd->query_passwd_state_timeout_id) {
g_source_remove(sd->query_passwd_state_timeout_id);
sd->query_passwd_state_timeout_id = 0;
}
if (passwd_state != OFONO_SIM_PASSWORD_INVALID) {
DBG_(sd, "%d", passwd_state);
sd->query_passwd_state_cb = NULL;
sd->query_passwd_state_cb_data = NULL;
sd->ofono_passwd_state = passwd_state;
cb(ril_error_ok(&error), passwd_state, data);
if (!sd->query_passwd_state_sim_status_refresh_id) {
ril_sim_card_remove_handler(sd->card,
sd->query_passwd_state_sim_status_refresh_id);
sd->query_passwd_state_sim_status_refresh_id = 0;
}
/* Always request fresh status, just in case. */
ril_sim_card_request_status(sd->card);
sd->query_passwd_state_cb = cb;
sd->query_passwd_state_cb_data = data;
if (ril_sim_passwd_state(sd) != OFONO_SIM_PASSWORD_INVALID) {
/* Just wait for GET_SIM_STATUS completion */
DBG_(sd, "waiting for SIM status query to complete");
sd->query_passwd_state_sim_status_refresh_id =
ril_sim_card_add_status_received_handler(sd->card,
ril_sim_query_passwd_state_complete_cb, sd);
} else {
/* Wait for the state to change */
DBG_(sd, "waiting for the SIM state to change");
sd->query_passwd_state_cb = cb;
sd->query_passwd_state_cb_data = data;
sd->query_passwd_state_timeout_id =
g_timeout_add_seconds(SIM_STATE_CHANGE_TIMEOUT_SECS,
ril_sim_query_passwd_state_timeout_cb, sd);
}
/*
* We still need to complete the request somehow, even if
* GET_STATUS never completes or SIM status never changes.
*/
sd->query_passwd_state_timeout_id =
g_timeout_add_seconds(SIM_STATE_CHANGE_TIMEOUT_SECS,
ril_sim_query_passwd_state_timeout_cb, sd);
}
static gboolean ril_sim_pin_change_state_timeout_cb(gpointer user_data)
@@ -980,20 +1110,30 @@ static void ril_sim_pin_change_state_cb(GRilIoChannel *io, int ril_status,
struct ril_sim_pin_cbd *cbd = user_data;
struct ril_sim *sd = cbd->sd;
const int retry_count = ril_sim_parse_retry_count(data, len);
enum ofono_sim_password_type type = cbd->passwd_type;
DBG_(sd, "result=%d passwd_type=%d retry_count=%d",
ril_status, cbd->passwd_type, retry_count);
if (ril_status == RIL_E_SUCCESS && retry_count == 0 &&
sd->empty_pin_query_allowed &&
(cbd->passwd_type == OFONO_SIM_PASSWORD_SIM_PIN ||
cbd->passwd_type == OFONO_SIM_PASSWORD_SIM_PIN2)) {
/* Will query it */
sd->retries[cbd->passwd_type] = -1;
if (ril_status == RIL_E_SUCCESS && retry_count == 0) {
enum ofono_sim_password_type associated_pin =
__ofono_sim_puk2pin(type);
/*
* If PIN/PUK request has succeeded, zero retry count
* makes no sense, we have to assume that it's unknown.
* If it can be queried, it will be queried later. If
* it can't be queried it will remain unknown.
*/
sd->retries[type] = -1;
if (associated_pin != OFONO_SIM_PASSWORD_INVALID) {
/* Successful PUK requests affect PIN retry count */
sd->retries[associated_pin] = -1;
}
} else {
sd->retries[cbd->passwd_type] = retry_count;
sd->retries[type] = retry_count;
}
ril_sim_check_perm_lock(sd);
cbd->ril_status = ril_status;
if (cbd->card_status_id && (!cbd->state_event_count ||
ril_sim_app_in_transient_state(sd))) {
@@ -1023,6 +1163,13 @@ static void ril_sim_pin_change_state_cb(GRilIoChannel *io, int ril_status,
} else {
cbd->cb(ril_error_failure(&error), cbd->data);
}
/* To avoid assert in ril_sim_pin_req_done: */
if (cbd->card_status_id) {
ril_sim_card_remove_handler(cbd->card,
cbd->card_status_id);
cbd->card_status_id = 0;
}
}
}
@@ -1030,18 +1177,21 @@ static void ril_sim_pin_send(struct ofono_sim *sim, const char *passwd,
ofono_sim_lock_unlock_cb_t cb, void *data)
{
struct ril_sim *sd = ril_sim_get_data(sim);
GRilIoRequest *req = grilio_request_new();
GRilIoRequest *req = ril_sim_enter_sim_pin_req(sd, passwd);
grilio_request_append_int32(req, ENTER_SIM_PIN_PARAMS);
grilio_request_append_utf8(req, passwd);
grilio_request_append_utf8(req, ril_sim_app_id(sd));
if (req) {
DBG_(sd, "%s,aid=%s", passwd, ril_sim_app_id(sd));
grilio_queue_send_request_full(sd->q, req,
RIL_REQUEST_ENTER_SIM_PIN, ril_sim_pin_change_state_cb,
ril_sim_pin_req_done, ril_sim_pin_cbd_new(sd,
OFONO_SIM_PASSWORD_SIM_PIN, TRUE, cb, data));
grilio_request_unref(req);
} else {
struct ofono_error error;
DBG_(sd, "%s,aid=%s", passwd, ril_sim_app_id(sd));
grilio_queue_send_request_full(sd->q, req, RIL_REQUEST_ENTER_SIM_PIN,
ril_sim_pin_change_state_cb, ril_sim_pin_req_done,
ril_sim_pin_cbd_new(sd, OFONO_SIM_PASSWORD_SIM_PIN,
TRUE, cb, data));
grilio_request_unref(req);
DBG_(sd, "sorry");
cb(ril_error_failure(&error), data);
}
}
static guint ril_perso_change_state(struct ofono_sim *sim,
@@ -1109,26 +1259,23 @@ static void ril_sim_pin_change_state(struct ofono_sim *sim,
const char *passwd, ofono_sim_lock_unlock_cb_t cb, void *data)
{
struct ril_sim *sd = ril_sim_get_data(sim);
struct ofono_error error;
const char *app_id = ril_sim_app_id(sd);
const char *type_str = ril_sim_facility_code(passwd_type);
struct ofono_error error;
guint id = 0;
DBG_(sd, "%d,%s,%d,%s,0,aid=%s", passwd_type, type_str, enable, passwd,
ril_sim_app_id(sd));
DBG_(sd, "%d,%s,%d,%s,0,aid=%s", passwd_type, type_str,
enable, passwd, app_id);
if (passwd_type == OFONO_SIM_PASSWORD_PHNET_PIN) {
id = ril_perso_change_state(sim, passwd_type, enable, passwd,
cb, data);
} else if (type_str) {
GRilIoRequest *req = grilio_request_new();
grilio_request_append_int32(req, SET_FACILITY_LOCK_PARAMS);
grilio_request_append_utf8(req, type_str);
grilio_request_append_utf8(req, enable ?
RIL_FACILITY_LOCK : RIL_FACILITY_UNLOCK);
grilio_request_append_utf8(req, passwd);
grilio_request_append_utf8(req, "0"); /* class */
grilio_request_append_utf8(req, ril_sim_app_id(sd));
GRilIoRequest *req = grilio_request_array_utf8_new(5, type_str,
enable ? RIL_FACILITY_LOCK : RIL_FACILITY_UNLOCK,
passwd, "0" /* class */, app_id);
grilio_request_set_blocking(req, TRUE);
id = grilio_queue_send_request_full(sd->q, req,
RIL_REQUEST_SET_FACILITY_LOCK,
ril_sim_pin_change_state_cb, ril_sim_pin_req_done,
@@ -1146,19 +1293,22 @@ static void ril_sim_pin_send_puk(struct ofono_sim *sim,
ofono_sim_lock_unlock_cb_t cb, void *data)
{
struct ril_sim *sd = ril_sim_get_data(sim);
GRilIoRequest *req = grilio_request_sized_new(60);
GRilIoRequest *req = ril_sim_enter_sim_puk_req(sd, puk, passwd);
grilio_request_append_int32(req, ENTER_SIM_PUK_PARAMS);
grilio_request_append_utf8(req, puk);
grilio_request_append_utf8(req, passwd);
grilio_request_append_utf8(req, ril_sim_app_id(sd));
if (req) {
DBG_(sd, "puk=%s,pin=%s,aid=%s", puk, passwd,
ril_sim_app_id(sd));
grilio_queue_send_request_full(sd->q, req,
RIL_REQUEST_ENTER_SIM_PUK, ril_sim_pin_change_state_cb,
ril_sim_pin_req_done, ril_sim_pin_cbd_new(sd,
OFONO_SIM_PASSWORD_SIM_PUK, TRUE, cb, data));
grilio_request_unref(req);
} else {
struct ofono_error error;
DBG_(sd, "puk=%s,pin=%s,aid=%s", puk, passwd, ril_sim_app_id(sd));
grilio_queue_send_request_full(sd->q, req, RIL_REQUEST_ENTER_SIM_PUK,
ril_sim_pin_change_state_cb, ril_sim_pin_req_done,
ril_sim_pin_cbd_new(sd, OFONO_SIM_PASSWORD_SIM_PUK,
TRUE, cb, data));
grilio_request_unref(req);
DBG_(sd, "sorry");
cb(ril_error_failure(&error), data);
}
}
static void ril_sim_change_passwd(struct ofono_sim *sim,
@@ -1167,15 +1317,12 @@ static void ril_sim_change_passwd(struct ofono_sim *sim,
ofono_sim_lock_unlock_cb_t cb, void *data)
{
struct ril_sim *sd = ril_sim_get_data(sim);
GRilIoRequest *req = grilio_request_sized_new(60);
const char *app_id = ril_sim_app_id(sd);
GRilIoRequest *req = grilio_request_array_utf8_new(3,
old_passwd, new_passwd, app_id);
grilio_request_append_int32(req, CHANGE_SIM_PIN_PARAMS);
grilio_request_append_utf8(req, old_passwd);
grilio_request_append_utf8(req, new_passwd);
grilio_request_append_utf8(req, ril_sim_app_id(sd));
DBG_(sd, "old=%s,new=%s,aid=%s", old_passwd, new_passwd,
ril_sim_app_id(sd));
DBG_(sd, "old=%s,new=%s,aid=%s", old_passwd, new_passwd, app_id);
grilio_request_set_blocking(req, TRUE);
grilio_queue_send_request_full(sd->q, req,
(passwd_type == OFONO_SIM_PASSWORD_SIM_PIN2) ?
RIL_REQUEST_CHANGE_SIM_PIN2 : RIL_REQUEST_CHANGE_SIM_PIN,
@@ -1191,6 +1338,8 @@ static void ril_sim_query_facility_lock_cb(GRilIoChannel *io, int status,
struct ril_sim_cbd *cbd = user_data;
ofono_query_facility_lock_cb_t cb = cbd->cb.query_facility_lock;
ril_sim_card_sim_io_finished(cbd->sd->card, cbd->req_id);
if (status == RIL_E_SUCCESS) {
int locked = 0;
GRilIoParser rilp;
@@ -1212,18 +1361,16 @@ static void ril_sim_query_facility_lock(struct ofono_sim *sim,
ofono_query_facility_lock_cb_t cb, void *data)
{
struct ril_sim *sd = ril_sim_get_data(sim);
GRilIoRequest *req = grilio_request_new();
const char *type_str = ril_sim_facility_code(type);
struct ril_sim_cbd *cbd = ril_sim_cbd_new(sd, cb, data);
GRilIoRequest *req = grilio_request_array_utf8_new(4,
type_str, "", "0" /* class */, ril_sim_app_id(sd));
DBG_(sd, "%s", type_str);
grilio_request_append_int32(req, 4);
grilio_request_append_utf8(req, type_str);
grilio_request_append_utf8(req, "");
grilio_request_append_utf8(req, "0"); /* class */
grilio_request_append_utf8(req, ril_sim_app_id(sd));
grilio_queue_send_request_full(sd->q, req,
cbd->req_id = grilio_queue_send_request_full(sd->q, req,
RIL_REQUEST_QUERY_FACILITY_LOCK, ril_sim_query_facility_lock_cb,
ril_sim_cbd_free, ril_sim_cbd_new(sd, cb, data));
ril_sim_cbd_free, cbd);
ril_sim_card_sim_io_started(sd->card, cbd->req_id);
grilio_request_unref(req);
}
@@ -1238,11 +1385,15 @@ static gboolean ril_sim_register(gpointer user)
ofono_sim_register(sd->sim);
/* Register for change notifications */
sd->card_status_id = ril_sim_card_add_status_changed_handler(sd->card,
ril_sim_status_cb, sd);
sd->card_event_id[SIM_CARD_STATUS_EVENT] =
ril_sim_card_add_status_changed_handler(sd->card,
ril_sim_status_changed_cb, sd);
sd->card_event_id[SIM_CARD_APP_EVENT] =
ril_sim_card_add_app_changed_handler(sd->card,
ril_sim_app_changed_cb, sd);
/* Check the current state */
ril_sim_status_cb(sd->card, sd);
ril_sim_status_changed_cb(sd->card, sd);
return FALSE;
}
@@ -1289,7 +1440,13 @@ static void ril_sim_remove(struct ofono_sim *sim)
g_source_remove(sd->query_passwd_state_timeout_id);
}
ril_sim_card_remove_handler(sd->card, sd->card_status_id);
if (sd->query_passwd_state_sim_status_refresh_id) {
ril_sim_card_remove_handler(sd->card,
sd->query_passwd_state_sim_status_refresh_id);
}
ril_sim_card_remove_handlers(sd->card, sd->card_event_id,
G_N_ELEMENTS(sd->card_event_id));
ril_sim_card_unref(sd->card);
grilio_channel_unref(sd->io);

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2016 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -24,6 +24,15 @@
#include <gutil_misc.h>
#define UICC_SUBSCRIPTION_TIMEOUT_MS (30000)
/* SIM I/O idle timeout is measured in the number of idle loops.
* When active SIM I/O is going on, the idle loop count very rarely
* exceeds 1 between the requests, so 10 is more than enough. Idle
* loop is actually more accurate criteria than a timeout because
* it doesn't depend that much on the system load. */
#define SIM_IO_IDLE_LOOPS (10)
typedef GObjectClass RilSimCardClass;
typedef struct ril_sim_card RilSimCard;
@@ -38,7 +47,11 @@ struct ril_sim_card_priv {
GRilIoQueue *q;
int flags;
guint status_req_id;
guint sub_req_id;
gulong event_id[EVENT_COUNT];
guint sim_io_idle_id;
guint sim_io_idle_count;
GHashTable* sim_io_pending;
};
enum ril_sim_card_signal {
@@ -46,13 +59,15 @@ enum ril_sim_card_signal {
SIGNAL_STATUS_CHANGED,
SIGNAL_STATE_CHANGED,
SIGNAL_APP_CHANGED,
SIGNAL_SIM_IO_ACTIVE_CHANGED,
SIGNAL_COUNT
};
#define SIGNAL_STATUS_RECEIVED_NAME "ril-simcard-status-received"
#define SIGNAL_STATUS_CHANGED_NAME "ril-simcard-status-changed"
#define SIGNAL_STATE_CHANGED_NAME "ril-simcard-state-changed"
#define SIGNAL_APP_CHANGED_NAME "ril-simcard-app-changed"
#define SIGNAL_STATUS_RECEIVED_NAME "ril-simcard-status-received"
#define SIGNAL_STATUS_CHANGED_NAME "ril-simcard-status-changed"
#define SIGNAL_STATE_CHANGED_NAME "ril-simcard-state-changed"
#define SIGNAL_APP_CHANGED_NAME "ril-simcard-app-changed"
#define SIGNAL_SIM_IO_ACTIVE_CHANGED_NAME "ril-simcard-sim-io-active-changed"
static guint ril_sim_card_signals[SIGNAL_COUNT] = { 0 };
@@ -61,11 +76,16 @@ G_DEFINE_TYPE(RilSimCard, ril_sim_card, G_TYPE_OBJECT)
#define RIL_SIMCARD(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), \
RIL_SIMCARD_TYPE, RilSimCard))
#define NEW_SIGNAL(klass,name) NEW_SIGNAL_(klass,name##_CHANGED)
#define NEW_SIGNAL_(klass,name) \
ril_sim_card_signals[SIGNAL_##name] = \
g_signal_new(SIGNAL_##name##_NAME, \
G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST, \
0, NULL, NULL, NULL, G_TYPE_NONE, 0)
#define RIL_SIMCARD_STATE_CHANGED (0x01)
#define RIL_SIMCARD_STATUS_CHANGED (0x02)
static void ril_sim_card_request_status(struct ril_sim_card *self);
static gboolean ril_sim_card_app_equal(const struct ril_sim_card_app *a1,
const struct ril_sim_card_app *a2)
{
@@ -137,22 +157,65 @@ static void ril_sim_card_status_free(struct ril_sim_card_status *status)
}
}
static void ril_sim_card_subscribe(struct ril_sim_card *self,
int app_index, int sub_status)
static void ril_sim_card_subscription_done(struct ril_sim_card *self)
{
struct ril_sim_card_priv *priv = self->priv;
if (priv->sub_req_id) {
/* Some RILs never reply to SET_UICC_SUBSCRIPTION requst,
* so we better drop rather than cancel it (so that it gets
* removed from the list of pending requests) */
grilio_channel_drop_request(priv->io, priv->sub_req_id);
priv->sub_req_id = 0;
}
grilio_queue_transaction_finish(priv->q);
}
static void ril_sim_card_subscribe_cb(GRilIoChannel* io, int status,
const void* data, guint len, void* user_data)
{
struct ril_sim_card *self = RIL_SIMCARD(user_data);
struct ril_sim_card_priv *priv = self->priv;
GASSERT(status == GRILIO_STATUS_OK);
GASSERT(priv->sub_req_id);
priv->sub_req_id = 0;
DBG("UICC subscription OK for slot %u", self->slot);
ril_sim_card_subscription_done(self);
}
static void ril_sim_card_subscribe(struct ril_sim_card *self, int app_index,
enum ril_uicc_subscription_action sub_action)
{
struct ril_sim_card_priv *priv = self->priv;
GRilIoRequest *req = grilio_request_sized_new(16);
const guint sub_id = self->slot;
guint code;
DBG("%u,%d,%u,%d", self->slot, app_index, sub_id, sub_status);
DBG("%u,%d,%u,%d", self->slot, app_index, sub_id, sub_action);
grilio_request_append_int32(req, self->slot);
grilio_request_append_int32(req, app_index);
grilio_request_append_int32(req, sub_id);
grilio_request_append_int32(req, sub_status);
grilio_queue_send_request(priv->q, req, (priv->io->ril_version <= 9 &&
grilio_request_append_int32(req, sub_action);
grilio_request_set_retry(req, 0, -1);
grilio_request_set_timeout(req, UICC_SUBSCRIPTION_TIMEOUT_MS);
code = (priv->io->ril_version <= 9 &&
(priv->flags & RIL_SIM_CARD_V9_UICC_SUBSCRIPTION_WORKAROUND)) ?
RIL_REQUEST_V9_SET_UICC_SUBSCRIPTION :
RIL_REQUEST_SET_UICC_SUBSCRIPTION);
RIL_REQUEST_V9_SET_UICC_SUBSCRIPTION :
RIL_REQUEST_SET_UICC_SUBSCRIPTION;
if (priv->sub_req_id) {
/* Some RILs never reply to SET_UICC_SUBSCRIPTION requst,
* so we better drop rather than cancel it (so that it gets
* removed from the list of pending requests) */
grilio_channel_drop_request(priv->io, priv->sub_req_id);
}
/* Don't allow any requests other that GET_SIM_STATUS until
* we are done with the subscription */
grilio_queue_transaction_start(priv->q);
priv->sub_req_id = grilio_queue_send_request_full(priv->q,
req, code, ril_sim_card_subscribe_cb, NULL, self);
grilio_request_unref(req);
}
@@ -185,14 +248,17 @@ static void ril_sim_card_update_app(struct ril_sim_card *self)
if (status->gsm_umts_index >= 0 &&
status->gsm_umts_index < status->num_apps) {
app_index = status->gsm_umts_index;
ril_sim_card_subscription_done(self);
} else {
app_index = ril_sim_card_select_app(status);
if (app_index >= 0) {
ril_sim_card_subscribe(self, app_index, 1);
ril_sim_card_subscribe(self, app_index,
RIL_UICC_SUBSCRIPTION_ACTIVATE);
}
}
} else {
app_index = -1;
ril_sim_card_subscription_done(self);
}
if (app_index >= 0 &&
@@ -203,8 +269,8 @@ static void ril_sim_card_update_app(struct ril_sim_card *self)
}
if (!ril_sim_card_app_equal(old_app, self->app)) {
g_signal_emit(self,
ril_sim_card_signals[SIGNAL_APP_CHANGED], 0);
g_signal_emit(self, ril_sim_card_signals
[SIGNAL_APP_CHANGED], 0);
}
}
@@ -218,23 +284,23 @@ static void ril_sim_card_update_status(struct ril_sim_card *self,
self->status = status;
ril_sim_card_update_app(self);
g_signal_emit(self, ril_sim_card_signals[
SIGNAL_STATUS_RECEIVED], 0);
g_signal_emit(self, ril_sim_card_signals
[SIGNAL_STATUS_RECEIVED], 0);
if (diff & RIL_SIMCARD_STATUS_CHANGED) {
DBG("status changed");
g_signal_emit(self, ril_sim_card_signals[
SIGNAL_STATUS_CHANGED], 0);
g_signal_emit(self, ril_sim_card_signals
[SIGNAL_STATUS_CHANGED], 0);
}
if (diff & RIL_SIMCARD_STATE_CHANGED) {
DBG("state changed");
g_signal_emit(self, ril_sim_card_signals[
SIGNAL_STATE_CHANGED], 0);
g_signal_emit(self, ril_sim_card_signals
[SIGNAL_STATE_CHANGED], 0);
}
ril_sim_card_status_free(old_status);
} else {
ril_sim_card_status_free(status);
g_signal_emit(self, ril_sim_card_signals[
SIGNAL_STATUS_RECEIVED], 0);
g_signal_emit(self, ril_sim_card_signals
[SIGNAL_STATUS_RECEIVED], 0);
}
}
@@ -317,7 +383,8 @@ static struct ril_sim_card_status *ril_sim_card_status_parse(const void *data,
status->num_apps = num_apps;
if (num_apps > 0) {
status->apps = g_new0(struct ril_sim_card_app, num_apps);
status->apps =
g_new0(struct ril_sim_card_app, num_apps);
}
for (i = 0; i < num_apps; i++) {
@@ -338,6 +405,7 @@ static struct ril_sim_card_status *ril_sim_card_status_parse(const void *data,
}
if (i == num_apps) {
GASSERT(grilio_parser_at_end(&rilp));
return status;
} else {
ril_sim_card_status_free(status);
@@ -349,7 +417,7 @@ static struct ril_sim_card_status *ril_sim_card_status_parse(const void *data,
static void ril_sim_card_status_cb(GRilIoChannel *io, int ril_status,
const void *data, guint len, void *user_data)
{
struct ril_sim_card *self = user_data;
struct ril_sim_card *self = RIL_SIMCARD(user_data);
struct ril_sim_card_priv *priv = self->priv;
GASSERT(priv->status_req_id);
@@ -365,28 +433,105 @@ static void ril_sim_card_status_cb(GRilIoChannel *io, int ril_status,
}
}
static void ril_sim_card_request_status(struct ril_sim_card *self)
void ril_sim_card_request_status(struct ril_sim_card *self)
{
struct ril_sim_card_priv *priv = self->priv;
if (G_LIKELY(self)) {
struct ril_sim_card_priv *priv = self->priv;
if (priv->status_req_id) {
/* Retry right away, don't wait for retry timeout to expire */
grilio_channel_retry_request(priv->io, priv->status_req_id);
} else {
GRilIoRequest* req = grilio_request_new();
if (priv->status_req_id) {
/* Retry right away, don't wait for retry
* timeout to expire */
grilio_channel_retry_request(priv->io,
priv->status_req_id);
} else {
GRilIoRequest* req = grilio_request_new();
grilio_request_set_retry(req, RIL_RETRY_SECS*1000, -1);
priv->status_req_id = grilio_queue_send_request_full(priv->q,
grilio_request_set_retry(req, RIL_RETRY_SECS*1000, -1);
priv->status_req_id =
grilio_queue_send_request_full(priv->q,
req, RIL_REQUEST_GET_SIM_STATUS,
ril_sim_card_status_cb, NULL, self);
grilio_request_unref(req);
grilio_request_unref(req);
}
}
}
static void ril_sim_card_update_sim_io_active(struct ril_sim_card *self)
{
/* SIM I/O is considered active for certain period of time after
* the last request has completed. That's because SIM_IO requests
* are usually submitted in large quantities and quick succession.
* Some RILs don't like being bothered while they are doing SIM I/O
* and some time after that too. That sucks but what else can we
* do about it? */
struct ril_sim_card_priv *priv = self->priv;
const gboolean active = priv->sim_io_idle_id ||
g_hash_table_size(priv->sim_io_pending);
if (self->sim_io_active != active) {
self->sim_io_active = active;
DBG("SIM I/O for slot %u is %sactive", self->slot,
active ? "" : "in");
g_signal_emit(self, ril_sim_card_signals
[SIGNAL_SIM_IO_ACTIVE_CHANGED], 0);
}
}
void ril_sim_card_sim_io_started(struct ril_sim_card *self, guint id)
{
if (G_LIKELY(self) && G_LIKELY(id)) {
struct ril_sim_card_priv *priv = self->priv;
gpointer key = GINT_TO_POINTER(id);
g_hash_table_insert(priv->sim_io_pending, key, key);
if (priv->sim_io_idle_id) {
g_source_remove(priv->sim_io_idle_id);
priv->sim_io_idle_id = 0;
priv->sim_io_idle_count = 0;
}
ril_sim_card_update_sim_io_active(self);
}
}
static gboolean ril_sim_card_sim_io_idle_cb(gpointer user_data)
{
struct ril_sim_card *self = RIL_SIMCARD(user_data);
struct ril_sim_card_priv *priv = self->priv;
if (++(priv->sim_io_idle_count) >= SIM_IO_IDLE_LOOPS) {
priv->sim_io_idle_id = 0;
priv->sim_io_idle_count = 0;
ril_sim_card_update_sim_io_active(self);
return G_SOURCE_REMOVE;
} else {
return G_SOURCE_CONTINUE;
}
}
void ril_sim_card_sim_io_finished(struct ril_sim_card *self, guint id)
{
if (G_LIKELY(self) && G_LIKELY(id)) {
struct ril_sim_card_priv *priv = self->priv;
gpointer key = GINT_TO_POINTER(id);
if (g_hash_table_remove(priv->sim_io_pending, key) &&
!g_hash_table_size(priv->sim_io_pending)) {
/* Reset the idle loop count */
if (priv->sim_io_idle_id) {
g_source_remove(priv->sim_io_idle_id);
priv->sim_io_idle_count = 0;
}
priv->sim_io_idle_id =
g_idle_add(ril_sim_card_sim_io_idle_cb, self);
}
ril_sim_card_update_sim_io_active(self);
}
}
static void ril_sim_card_status_changed(GRilIoChannel *io, guint code,
const void *data, guint len, void *user_data)
{
struct ril_sim_card *self = user_data;
struct ril_sim_card *self = RIL_SIMCARD(user_data);
ril_sim_card_request_status(self);
}
@@ -474,6 +619,13 @@ gulong ril_sim_card_add_app_changed_handler(struct ril_sim_card *self,
SIGNAL_APP_CHANGED_NAME, G_CALLBACK(cb), arg) : 0;
}
gulong ril_sim_card_add_sim_io_active_changed_handler(struct ril_sim_card *self,
ril_sim_card_cb_t cb, void *arg)
{
return (G_LIKELY(self) && G_LIKELY(cb)) ? g_signal_connect(self,
SIGNAL_SIM_IO_ACTIVE_CHANGED_NAME, G_CALLBACK(cb), arg) : 0;
}
void ril_sim_card_remove_handler(struct ril_sim_card *self, gulong id)
{
if (G_LIKELY(self) && G_LIKELY(id)) {
@@ -488,8 +640,11 @@ void ril_sim_card_remove_handlers(struct ril_sim_card *self, gulong *ids, int n)
static void ril_sim_card_init(struct ril_sim_card *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, RIL_SIMCARD_TYPE,
struct ril_sim_card_priv);
struct ril_sim_card_priv *priv = G_TYPE_INSTANCE_GET_PRIVATE(self,
RIL_SIMCARD_TYPE, struct ril_sim_card_priv);
self->priv = priv;
priv->sim_io_pending = g_hash_table_new(g_direct_hash, g_direct_equal);
}
static void ril_sim_card_dispose(GObject *object)
@@ -507,6 +662,10 @@ static void ril_sim_card_finalize(GObject *object)
struct ril_sim_card *self = RIL_SIMCARD(object);
struct ril_sim_card_priv *priv = self->priv;
if (priv->sim_io_idle_id) {
g_source_remove(priv->sim_io_idle_id);
}
g_hash_table_destroy(priv->sim_io_pending);
grilio_channel_unref(priv->io);
grilio_queue_unref(priv->q);
ril_sim_card_status_free(self->status);
@@ -520,22 +679,11 @@ static void ril_sim_card_class_init(RilSimCardClass *klass)
object_class->dispose = ril_sim_card_dispose;
object_class->finalize = ril_sim_card_finalize;
g_type_class_add_private(klass, sizeof(struct ril_sim_card_priv));
ril_sim_card_signals[SIGNAL_STATUS_RECEIVED] =
g_signal_new(SIGNAL_STATUS_RECEIVED_NAME,
G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST,
0, NULL, NULL, NULL, G_TYPE_NONE, 0);
ril_sim_card_signals[SIGNAL_STATUS_CHANGED] =
g_signal_new(SIGNAL_STATUS_CHANGED_NAME,
G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST,
0, NULL, NULL, NULL, G_TYPE_NONE, 0);
ril_sim_card_signals[SIGNAL_STATE_CHANGED] =
g_signal_new(SIGNAL_STATE_CHANGED_NAME,
G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST,
0, NULL, NULL, NULL, G_TYPE_NONE, 0);
ril_sim_card_signals[SIGNAL_APP_CHANGED] =
g_signal_new(SIGNAL_APP_CHANGED_NAME,
G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST,
0, NULL, NULL, NULL, G_TYPE_NONE, 0);
NEW_SIGNAL_(klass,STATUS_RECEIVED);
NEW_SIGNAL(klass,STATUS);
NEW_SIGNAL(klass,STATE);
NEW_SIGNAL(klass,APP);
NEW_SIGNAL(klass,SIM_IO_ACTIVE);
}
/*

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2016 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -44,6 +44,7 @@ struct ril_sim_card {
struct ril_sim_card_priv *priv;
struct ril_sim_card_status *status;
const struct ril_sim_card_app *app;
gboolean sim_io_active;
guint slot;
};
@@ -55,6 +56,9 @@ typedef void (*ril_sim_card_cb_t)(struct ril_sim_card *sc, void *arg);
struct ril_sim_card *ril_sim_card_new(GRilIoChannel *io, guint slot, int flags);
struct ril_sim_card *ril_sim_card_ref(struct ril_sim_card *sc);
void ril_sim_card_unref(struct ril_sim_card *sc);
void ril_sim_card_request_status(struct ril_sim_card *sc);
void ril_sim_card_sim_io_started(struct ril_sim_card *sc, guint id);
void ril_sim_card_sim_io_finished(struct ril_sim_card *sc, guint id);
gboolean ril_sim_card_ready(struct ril_sim_card *sc);
gulong ril_sim_card_add_status_received_handler(struct ril_sim_card *sc,
ril_sim_card_cb_t cb, void *arg);
@@ -64,6 +68,8 @@ gulong ril_sim_card_add_state_changed_handler(struct ril_sim_card *sc,
ril_sim_card_cb_t cb, void *arg);
gulong ril_sim_card_add_app_changed_handler(struct ril_sim_card *sc,
ril_sim_card_cb_t cb, void *arg);
gulong ril_sim_card_add_sim_io_active_changed_handler(struct ril_sim_card *sc,
ril_sim_card_cb_t cb, void *arg);
void ril_sim_card_remove_handler(struct ril_sim_card *sc, gulong id);
void ril_sim_card_remove_handlers(struct ril_sim_card *sc, gulong *ids, int n);

View File

@@ -1,668 +0,0 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2016 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
* 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 "ril_sim_info.h"
#include "ril_network.h"
#include "ril_log.h"
#include <ofono/sim.h>
#include "ofono.h"
#include "storage.h"
#define RIL_SIM_INFO_STORE "cache"
#define RIL_SIM_INFO_STORE_GROUP "sim"
#define RIL_SIM_INFO_STORE_SPN "spn"
/* ICCID -> IMSI map */
#define RIL_SIM_ICCID_MAP "iccidmap"
#define RIL_SIM_ICCID_MAP_IMSI "imsi"
#define RIL_SIM_DEFAULT_SPN_BUFSIZE 8
G_STATIC_ASSERT(RIL_SIM_DEFAULT_SPN_BUFSIZE >= \
OFONO_MAX_MCC_LENGTH + OFONO_MAX_MNC_LENGTH + 1);
typedef GObjectClass RilSimInfoClass;
typedef struct ril_sim_info RilSimInfo;
typedef void (*ril_sim_info_remove_cb_t)(struct ofono_sim *sim,
unsigned int id);
typedef void (*ril_sim_info_set_value_cb_t)(struct ril_sim_info *info,
const char *value);
struct ril_sim_info_watch {
ril_sim_info_set_value_cb_t set_value;
ril_sim_info_remove_cb_t remove;
struct ril_sim_info *info;
unsigned int id;
};
struct ril_sim_info_priv {
char *log_prefix;
char *iccid;
char *imsi;
char *cached_spn;
char *sim_spn;
char *public_spn;
char default_spn[RIL_SIM_DEFAULT_SPN_BUFSIZE];
int public_spn_block;
struct ofono_sim *sim;
struct ril_sim_info_watch state_watch;
struct ril_sim_info_watch iccid_watch;
struct ril_sim_info_watch imsi_watch;
struct ril_sim_info_watch spn_watch;
struct ril_network *network;
gulong network_operator_changed_id;
gboolean update_imsi_cache;
gboolean update_iccid_map;
};
enum ril_sim_info_signal {
SIGNAL_ICCID_CHANGED,
SIGNAL_IMSI_CHANGED,
SIGNAL_SPN_CHANGED,
SIGNAL_COUNT
};
#define SIGNAL_ICCID_CHANGED_NAME "ril-sim-info-iccid-changed"
#define SIGNAL_IMSI_CHANGED_NAME "ril-sim-info-imsi-changed"
#define SIGNAL_SPN_CHANGED_NAME "ril-sim-info-spn-changed"
static guint ril_sim_info_signals[SIGNAL_COUNT] = { 0 };
G_DEFINE_TYPE(RilSimInfo, ril_sim_info, G_TYPE_OBJECT)
#define RIL_SIMINFO_TYPE (ril_sim_info_get_type())
#define RIL_SIMINFO(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\
RIL_SIMINFO_TYPE, RilSimInfo))
#define NEW_SIGNAL(klass,name) \
ril_sim_info_signals[SIGNAL_##name##_CHANGED] = \
g_signal_new(SIGNAL_##name##_CHANGED_NAME, \
G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST, \
0, NULL, NULL, NULL, G_TYPE_NONE, 0)
#define DBG_(info,fmt,args...) DBG("%s" fmt, (info)->priv->log_prefix, ##args)
static void ril_sim_info_signal_emit(struct ril_sim_info *self,
enum ril_sim_info_signal id)
{
g_signal_emit(self, ril_sim_info_signals[id], 0);
}
static void ril_sim_info_watch_remove(struct ril_sim_info_watch *watch)
{
if (watch->id) {
struct ril_sim_info_priv *priv = watch->info->priv;
GASSERT(priv->sim);
if (priv->sim) {
watch->remove(priv->sim, watch->id);
GASSERT(!watch->id);
}
watch->id = 0;
}
if (watch->set_value) {
watch->set_value(watch->info, NULL);
}
}
static void ril_sim_info_remove_spn_watch(struct ofono_sim *sim,
unsigned int id)
{
ofono_sim_remove_spn_watch(sim, &id);
}
static void ril_sim_info_update_imsi_cache(struct ril_sim_info *self)
{
struct ril_sim_info_priv *priv = self->priv;
if (priv->update_imsi_cache && priv->imsi && priv->imsi[0] &&
priv->cached_spn && priv->cached_spn[0]) {
gboolean save = FALSE;
const char *store = RIL_SIM_INFO_STORE;
GKeyFile *cache = storage_open(priv->imsi, store);
char *spn = g_key_file_get_string(cache,
RIL_SIM_INFO_STORE_GROUP,
RIL_SIM_INFO_STORE_SPN, NULL);
if (g_strcmp0(priv->cached_spn, spn)) {
save = TRUE;
g_key_file_set_string(cache, RIL_SIM_INFO_STORE_GROUP,
RIL_SIM_INFO_STORE_SPN, priv->cached_spn);
}
/*
* Since we are most likely running on flash which
* supports a limited number of writes, don't overwrite
* the file unless something has actually changed.
*/
if (save) {
DBG_(self, "updating " STORAGEDIR "/%s/%s",
priv->imsi, store);
storage_close(priv->imsi, store, cache, TRUE);
} else {
g_key_file_free(cache);
}
g_free(spn);
priv->update_imsi_cache = FALSE;
}
}
static void ril_sim_info_update_iccid_map(struct ril_sim_info *self)
{
struct ril_sim_info_priv *priv = self->priv;
if (priv->update_iccid_map && priv->iccid && priv->iccid[0] &&
priv->imsi && priv->imsi[0]) {
const char *store = RIL_SIM_ICCID_MAP;
GKeyFile *map = storage_open(NULL, store);
char *imsi = g_key_file_get_string(map, RIL_SIM_ICCID_MAP_IMSI,
priv->iccid, NULL);
/*
* Since we are most likely running on flash which
* supports a limited number of writes, don't overwrite
* the file unless something has actually changed.
*/
if (g_strcmp0(imsi, priv->imsi)) {
DBG_(self, "updating " STORAGEDIR "/%s", store);
g_key_file_set_string(map, RIL_SIM_ICCID_MAP_IMSI,
priv->iccid, priv->imsi);
storage_close(NULL, store, map, TRUE);
} else {
g_key_file_free(map);
}
g_free(imsi);
priv->update_iccid_map = FALSE;
}
}
static void ril_sim_info_set_imsi(struct ril_sim_info *self, const char *imsi)
{
struct ril_sim_info_priv *priv = self->priv;
if (g_strcmp0(priv->imsi, imsi)) {
g_free(priv->imsi);
self->imsi = priv->imsi = g_strdup(imsi);
priv->update_iccid_map = TRUE;
ril_sim_info_update_iccid_map(self);
ril_sim_info_update_imsi_cache(self);
ril_sim_info_signal_emit(self, SIGNAL_IMSI_CHANGED);
}
}
static void ril_sim_info_update_public_spn(struct ril_sim_info *self)
{
struct ril_sim_info_priv *priv = self->priv;
GASSERT(priv->public_spn_block >= 0);
if (!priv->public_spn_block) {
const char *spn = priv->sim_spn ? priv->sim_spn :
priv->cached_spn ? priv->cached_spn :
priv->default_spn;
if (g_strcmp0(priv->public_spn, spn)) {
g_free(priv->public_spn);
self->spn = priv->public_spn = g_strdup(spn);
ril_sim_info_signal_emit(self, SIGNAL_SPN_CHANGED);
}
}
}
static void ril_sim_info_set_cached_spn(struct ril_sim_info *self,
const char *spn)
{
struct ril_sim_info_priv *priv = self->priv;
if (g_strcmp0(priv->cached_spn, spn)) {
g_free(priv->cached_spn);
if (spn) {
DBG_(self, "cached spn \"%s\"", spn);
priv->cached_spn = g_strdup(spn);
priv->update_imsi_cache = TRUE;
ril_sim_info_update_imsi_cache(self);
} else {
priv->cached_spn = NULL;
}
ril_sim_info_update_public_spn(self);
}
}
static void ril_sim_info_set_sim_spn(struct ril_sim_info *self,
const char *spn)
{
struct ril_sim_info_priv *priv = self->priv;
if (g_strcmp0(priv->sim_spn, spn)) {
g_free(priv->sim_spn);
priv->sim_spn = g_strdup(spn);
priv->update_imsi_cache = TRUE;
ril_sim_info_set_cached_spn(self, spn);
ril_sim_info_update_imsi_cache(self);
ril_sim_info_update_public_spn(self);
}
}
static void ril_sim_info_update_default_spn(struct ril_sim_info *self)
{
struct ril_sim_info_priv *priv = self->priv;
char buf[RIL_SIM_DEFAULT_SPN_BUFSIZE];
const char *mcc = NULL;
const char *mnc = NULL;
if (priv->sim &&
ofono_sim_get_state(priv->sim) == OFONO_SIM_STATE_READY) {
mcc = ofono_sim_get_mcc(priv->sim);
mnc = ofono_sim_get_mnc(priv->sim);
}
if (mcc && mnc) {
snprintf(buf, RIL_SIM_DEFAULT_SPN_BUFSIZE, "%s%s", mcc, mnc);
buf[RIL_SIM_DEFAULT_SPN_BUFSIZE - 1] = 0;
} else {
buf[0] = 0;
}
if (strcmp(buf, priv->default_spn)) {
strncpy(priv->default_spn, buf, RIL_SIM_DEFAULT_SPN_BUFSIZE);
DBG_(self, "default spn \"%s\"", priv->default_spn);
ril_sim_info_update_public_spn(self);
}
}
static void ril_sim_info_network_check(struct ril_sim_info *self)
{
struct ril_sim_info_priv *priv = self->priv;
if (priv->network && priv->network->operator && priv->sim &&
ofono_sim_get_state(priv->sim) == OFONO_SIM_STATE_READY) {
const char *mcc = ofono_sim_get_mcc(priv->sim);
const char *mnc = ofono_sim_get_mnc(priv->sim);
const struct ofono_network_operator *op =
priv->network->operator;
if (mcc && mcc[0] && !strcmp(mcc, op->mcc) &&
mnc && mnc[0] && !strcmp(mnc, op->mnc)) {
/*
* If EFspn is present then sim_spn should be set
* before we get registered with the network.
*/
DBG_(self, "home network \"%s\"", op->name);
if (!priv->sim_spn) {
ril_sim_info_set_cached_spn(self, op->name);
}
}
}
}
static void ril_sim_info_network_operator_changed(struct ril_network *network,
void *user_data)
{
struct ril_sim_info *self = RIL_SIMINFO(user_data);
DBG_(self, "");
ril_sim_info_network_check(self);
}
static void ril_sim_info_load_cache(struct ril_sim_info *self)
{
struct ril_sim_info_priv *priv = self->priv;
if (priv->iccid && priv->iccid[0]) {
GKeyFile *map = storage_open(NULL, RIL_SIM_ICCID_MAP);
char *imsi = g_key_file_get_string(map, RIL_SIM_ICCID_MAP_IMSI,
priv->iccid, NULL);
g_key_file_free(map);
if (imsi && imsi[0] && g_strcmp0(priv->imsi, imsi)) {
if (priv->imsi && priv->imsi[0]) {
/* Need to update ICCID -> IMSI map */
DBG_(self, "IMSI changed %s -> %s",
priv->imsi, imsi);
priv->update_imsi_cache = TRUE;
}
g_free(priv->imsi);
self->imsi = priv->imsi = imsi;
DBG_(self, "imsi[%s] = %s", priv->iccid, imsi);
ril_sim_info_update_iccid_map(self);
ril_sim_info_signal_emit(self, SIGNAL_IMSI_CHANGED);
} else if (imsi) {
g_free(imsi);
} else {
DBG_(self, "no imsi for iccid %s", priv->iccid);
}
}
if (priv->imsi && priv->imsi[0]) {
GKeyFile *cache = storage_open(priv->imsi, RIL_SIM_INFO_STORE);
char *spn = g_key_file_get_string(cache,
RIL_SIM_INFO_STORE_GROUP,
RIL_SIM_INFO_STORE_SPN, NULL);
g_key_file_free(cache);
if (spn && spn[0] && g_strcmp0(priv->cached_spn, spn)) {
if (priv->cached_spn && priv->cached_spn[0]) {
/* Need to update the cache file */
DBG_(self, "spn changing %s -> %s",
priv->cached_spn, spn);
priv->update_imsi_cache = TRUE;
}
g_free(priv->cached_spn);
priv->cached_spn = spn;
DBG_(self, "spn[%s] = \"%s\"", priv->imsi, spn);
ril_sim_info_update_imsi_cache(self);
ril_sim_info_update_public_spn(self);
} else if (spn) {
g_free(spn);
} else {
DBG_(self, "no spn for imsi %s", priv->imsi);
}
}
}
static void ril_sim_info_set_iccid(struct ril_sim_info *self, const char *iccid)
{
struct ril_sim_info_priv *priv = self->priv;
if (g_strcmp0(priv->iccid, iccid)) {
g_free(priv->iccid);
self->iccid = priv->iccid = g_strdup(iccid);
ril_sim_info_signal_emit(self, SIGNAL_ICCID_CHANGED);
if (iccid) {
ril_sim_info_load_cache(self);
}
}
}
static void ril_sim_info_imsi_watch_cb(const char *imsi, void *data)
{
struct ril_sim_info_watch *watch = data;
DBG_(watch->info, "%s", imsi);
ril_sim_info_set_imsi(watch->info, imsi);
}
static void ril_sim_info_spn_watch_cb(const char *spn, const char *dc,
void *data)
{
struct ril_sim_info_watch *watch = data;
DBG_(watch->info, "%s", spn);
ril_sim_info_set_sim_spn(watch->info, spn);
}
static void ril_sim_info_iccid_watch_cb(const char *iccid, void *data)
{
struct ril_sim_info_watch *watch = data;
DBG_(watch->info, "%s", iccid);
ril_sim_info_set_iccid(watch->info, iccid);
}
static void ril_sim_info_watch_done(void *data)
{
struct ril_sim_info_watch *watch = data;
GASSERT(watch->id);
watch->id = 0;
}
static void ril_sim_info_handle_sim_state(struct ril_sim_info *self,
enum ofono_sim_state state)
{
struct ril_sim_info_priv *priv = self->priv;
struct ril_sim_info_watch *watch;
DBG_(self, "%d", state);
switch (state) {
case OFONO_SIM_STATE_READY:
/* SPN */
watch = &priv->spn_watch;
if (!watch->id) {
ofono_sim_add_spn_watch(priv->sim, &watch->id,
ril_sim_info_spn_watch_cb, watch,
ril_sim_info_watch_done);
GASSERT(priv->spn_watch.id);
}
/* IMSI */
watch = &priv->imsi_watch;
if (!watch->id) {
watch->id = ofono_sim_add_imsi_watch(priv->sim,
ril_sim_info_imsi_watch_cb, watch,
ril_sim_info_watch_done);
GASSERT(watch->id);
}
/* no break */
case OFONO_SIM_STATE_INSERTED:
case OFONO_SIM_STATE_LOCKED_OUT:
/* ICCID */
watch = &priv->iccid_watch;
if (!watch->id) {
watch->id = ofono_sim_add_iccid_watch(priv->sim,
ril_sim_info_iccid_watch_cb, watch,
ril_sim_info_watch_done);
GASSERT(watch->id);
}
break;
case OFONO_SIM_STATE_NOT_PRESENT:
case OFONO_SIM_STATE_RESETTING:
ril_sim_info_watch_remove(&priv->spn_watch);
ril_sim_info_watch_remove(&priv->imsi_watch);
ril_sim_info_watch_remove(&priv->iccid_watch);
break;
}
ril_sim_info_update_default_spn(self);
ril_sim_info_network_check(self);
}
static void ril_sim_info_state_watch_cb(enum ofono_sim_state new_state,
void *data)
{
struct ril_sim_info_watch *watch = data;
ril_sim_info_handle_sim_state(watch->info, new_state);
}
struct ril_sim_info *ril_sim_info_new(const char *log_prefix)
{
struct ril_sim_info *self = g_object_new(RIL_SIMINFO_TYPE, NULL);
self->priv->log_prefix = (log_prefix && log_prefix[0]) ?
g_strconcat(log_prefix, " ", NULL) : g_strdup("");
return self;
}
struct ril_sim_info *ril_sim_info_ref(struct ril_sim_info *self)
{
if (G_LIKELY(self)) {
g_object_ref(RIL_SIMINFO(self));
return self;
} else {
return NULL;
}
}
void ril_sim_info_unref(struct ril_sim_info *self)
{
if (G_LIKELY(self)) {
g_object_unref(RIL_SIMINFO(self));
}
}
void ril_sim_info_set_ofono_sim(struct ril_sim_info *self,
struct ofono_sim *sim)
{
if (G_LIKELY(self)) {
struct ril_sim_info_priv *priv = self->priv;
if (priv->sim != sim) {
priv->public_spn_block++;
ril_sim_info_watch_remove(&priv->state_watch);
ril_sim_info_watch_remove(&priv->iccid_watch);
ril_sim_info_watch_remove(&priv->imsi_watch);
ril_sim_info_watch_remove(&priv->spn_watch);
priv->update_imsi_cache = FALSE;
priv->update_iccid_map = FALSE;
priv->sim = sim;
if (sim) {
priv->state_watch.id =
ofono_sim_add_state_watch(sim,
ril_sim_info_state_watch_cb,
&priv->state_watch,
ril_sim_info_watch_done);
GASSERT(priv->state_watch.id);
DBG_(self, "attached to sim");
ril_sim_info_handle_sim_state(self,
ofono_sim_get_state(sim));
} else {
DBG_(self, "detached from sim");
ril_sim_info_update_default_spn(self);
ril_sim_info_network_check(self);
}
priv->public_spn_block--;
ril_sim_info_update_public_spn(self);
}
}
}
void ril_sim_info_set_network(struct ril_sim_info *self,
struct ril_network *network)
{
if (G_LIKELY(self) && self->priv->network != network) {
struct ril_sim_info_priv *priv = self->priv;
if (priv->network) {
ril_network_remove_handlers(priv->network,
&priv->network_operator_changed_id, 1);
ril_network_unref(priv->network);
}
if (network) {
priv->network_operator_changed_id =
ril_network_add_operator_changed_handler(network,
ril_sim_info_network_operator_changed,
self);
priv->network = ril_network_ref(network);
ril_sim_info_network_check(self);
} else {
priv->network = NULL;
}
}
}
gulong ril_sim_info_add_iccid_changed_handler(struct ril_sim_info *self,
ril_sim_info_cb_t cb, void *arg)
{
return (G_LIKELY(self) && G_LIKELY(cb)) ? g_signal_connect(self,
SIGNAL_ICCID_CHANGED_NAME, G_CALLBACK(cb), arg) : 0;
}
gulong ril_sim_info_add_imsi_changed_handler(struct ril_sim_info *self,
ril_sim_info_cb_t cb, void *arg)
{
return (G_LIKELY(self) && G_LIKELY(cb)) ? g_signal_connect(self,
SIGNAL_IMSI_CHANGED_NAME, G_CALLBACK(cb), arg) : 0;
}
gulong ril_sim_info_add_spn_changed_handler(struct ril_sim_info *self,
ril_sim_info_cb_t cb, void *arg)
{
return (G_LIKELY(self) && G_LIKELY(cb)) ? g_signal_connect(self,
SIGNAL_SPN_CHANGED_NAME, G_CALLBACK(cb), arg) : 0;
}
void ril_sim_info_remove_handler(struct ril_sim_info *self, gulong id)
{
if (G_LIKELY(self) && G_LIKELY(id)) {
g_signal_handler_disconnect(self, id);
}
}
static void ril_sim_info_watch_init(struct ril_sim_info *self,
struct ril_sim_info_watch *watch,
ril_sim_info_set_value_cb_t set_value,
ril_sim_info_remove_cb_t remove)
{
watch->info = self;
watch->set_value = set_value;
watch->remove = remove;
}
static void ril_sim_info_init(struct ril_sim_info *self)
{
struct ril_sim_info_priv *priv = G_TYPE_INSTANCE_GET_PRIVATE(self,
RIL_SIMINFO_TYPE, struct ril_sim_info_priv);
self->priv = priv;
ril_sim_info_watch_init(self, &priv->state_watch,
NULL, ofono_sim_remove_state_watch);
ril_sim_info_watch_init(self, &priv->iccid_watch,
ril_sim_info_set_iccid, ofono_sim_remove_iccid_watch);
ril_sim_info_watch_init(self, &priv->imsi_watch,
ril_sim_info_set_imsi, ofono_sim_remove_imsi_watch);
ril_sim_info_watch_init(self, &priv->spn_watch,
ril_sim_info_set_sim_spn, ril_sim_info_remove_spn_watch);
}
static void ril_sim_info_dispose(GObject *object)
{
struct ril_sim_info *self = RIL_SIMINFO(object);
ril_sim_info_set_ofono_sim(self, NULL);
ril_sim_info_set_network(self, NULL);
G_OBJECT_CLASS(ril_sim_info_parent_class)->dispose(object);
}
static void ril_sim_info_finalize(GObject *object)
{
struct ril_sim_info *self = RIL_SIMINFO(object);
struct ril_sim_info_priv *priv = self->priv;
g_free(priv->log_prefix);
g_free(priv->cached_spn);
g_free(priv->public_spn);
GASSERT(!priv->iccid);
GASSERT(!priv->imsi);
GASSERT(!priv->sim_spn);
G_OBJECT_CLASS(ril_sim_info_parent_class)->finalize(object);
}
static void ril_sim_info_class_init(RilSimInfoClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->dispose = ril_sim_info_dispose;
object_class->finalize = ril_sim_info_finalize;
g_type_class_add_private(klass, sizeof(struct ril_sim_info_priv));
NEW_SIGNAL(klass, ICCID);
NEW_SIGNAL(klass, IMSI);
NEW_SIGNAL(klass, SPN);
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -1,243 +0,0 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2016 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
* 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 "ril_plugin.h"
#include "ril_sim_info.h"
#include "ril_log.h"
#include <ofono/dbus.h>
#include <gdbus.h>
#include "ofono.h"
#include "storage.h"
enum sim_info_event_id {
SIM_INFO_EVENT_ICCID,
SIM_INFO_EVENT_IMSI,
SIM_INFO_EVENT_SPN,
SIM_INFO_EVENT_COUNT
};
struct ril_sim_info_dbus {
struct ril_modem *md;
struct ril_sim_info *info;
DBusConnection *conn;
char *path;
gulong handler_id[SIM_INFO_EVENT_COUNT];
};
#define RIL_SIM_INFO_DBUS_INTERFACE "org.nemomobile.ofono.SimInfo"
#define RIL_SIM_INFO_DBUS_INTERFACE_VERSION (1)
#define RIL_SIM_INFO_DBUS_ICCID_CHANGED_SIGNAL "CardIdentifierChanged"
#define RIL_SIM_INFO_DBUS_IMSI_CHANGED_SIGNAL "SubscriberIdentityChanged"
#define RIL_SIM_INFO_DBUS_SPN_CHANGED_SIGNAL "ServiceProviderNameChanged"
static void ril_sim_info_dbus_append_string(DBusMessageIter *it, const char *s)
{
if (!s) s = "";
dbus_message_iter_append_basic(it, DBUS_TYPE_STRING, &s);
}
static DBusMessage *ril_sim_info_dbus_reply_with_string(DBusMessage *msg,
const char *str)
{
DBusMessage *reply = dbus_message_new_method_return(msg);
DBusMessageIter iter;
dbus_message_iter_init_append(reply, &iter);
ril_sim_info_dbus_append_string(&iter, str);
return reply;
}
static DBusMessage *ril_sim_info_dbus_get_all(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ril_sim_info_dbus *dbus = data;
struct ril_sim_info *info = dbus->info;
DBusMessage *reply = dbus_message_new_method_return(msg);
const dbus_int32_t version = RIL_SIM_INFO_DBUS_INTERFACE_VERSION;
DBusMessageIter iter;
dbus_message_iter_init_append(reply, &iter);
dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &version);
ril_sim_info_dbus_append_string(&iter, info->iccid);
ril_sim_info_dbus_append_string(&iter, info->imsi);
ril_sim_info_dbus_append_string(&iter, info->spn);
return reply;
}
static DBusMessage *ril_sim_info_dbus_get_version(DBusConnection *dc,
DBusMessage *msg, void *data)
{
DBusMessage *reply = dbus_message_new_method_return(msg);
dbus_int32_t version = RIL_SIM_INFO_DBUS_INTERFACE_VERSION;
DBusMessageIter iter;
dbus_message_iter_init_append(reply, &iter);
dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &version);
return reply;
}
static DBusMessage *ril_sim_info_dbus_get_iccid(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ril_sim_info_dbus *dbus = data;
return ril_sim_info_dbus_reply_with_string(msg, dbus->info->iccid);
}
static DBusMessage *ril_sim_info_dbus_get_imsi(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ril_sim_info_dbus *dbus = data;
return ril_sim_info_dbus_reply_with_string(msg, dbus->info->imsi);
}
static DBusMessage *ril_sim_info_dbus_get_spn(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ril_sim_info_dbus *dbus = data;
return ril_sim_info_dbus_reply_with_string(msg, dbus->info->spn);
}
static const GDBusMethodTable ril_sim_info_dbus_methods[] = {
{ GDBUS_METHOD("GetAll",
NULL, GDBUS_ARGS({"version", "i" },
{"iccid", "s" },
{"imsi", "s" },
{"spn" , "s"}),
ril_sim_info_dbus_get_all) },
{ GDBUS_METHOD("GetInterfaceVersion",
NULL, GDBUS_ARGS({ "version", "i" }),
ril_sim_info_dbus_get_version) },
{ GDBUS_METHOD("GetCardIdentifier",
NULL, GDBUS_ARGS({ "iccid", "s" }),
ril_sim_info_dbus_get_iccid) },
{ GDBUS_METHOD("GetSubscriberIdentity",
NULL, GDBUS_ARGS({ "imsi", "s" }),
ril_sim_info_dbus_get_imsi) },
{ GDBUS_METHOD("GetServiceProviderName",
NULL, GDBUS_ARGS({ "spn", "s" }),
ril_sim_info_dbus_get_spn) },
{ }
};
static const GDBusSignalTable ril_sim_info_dbus_signals[] = {
{ GDBUS_SIGNAL(RIL_SIM_INFO_DBUS_ICCID_CHANGED_SIGNAL,
GDBUS_ARGS({ "iccid", "s" })) },
{ GDBUS_SIGNAL(RIL_SIM_INFO_DBUS_IMSI_CHANGED_SIGNAL,
GDBUS_ARGS({ "imsi", "s" })) },
{ GDBUS_SIGNAL(RIL_SIM_INFO_DBUS_SPN_CHANGED_SIGNAL,
GDBUS_ARGS({ "spn", "s" })) },
{ }
};
static void ril_sim_info_dbus_emit(struct ril_sim_info_dbus *dbus,
const char *signal, const char *value)
{
const char *arg = value;
if (!arg) arg = "";
g_dbus_emit_signal(dbus->conn, dbus->path, RIL_SIM_INFO_DBUS_INTERFACE,
signal, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID);
}
static void ril_sim_info_dbus_iccid_cb(struct ril_sim_info *info, void *arg)
{
struct ril_sim_info_dbus *dbus = arg;
ril_sim_info_dbus_emit(dbus, RIL_SIM_INFO_DBUS_ICCID_CHANGED_SIGNAL,
info->iccid);
}
static void ril_sim_info_dbus_imsi_cb(struct ril_sim_info *info, void *arg)
{
struct ril_sim_info_dbus *dbus = arg;
ril_sim_info_dbus_emit(dbus, RIL_SIM_INFO_DBUS_IMSI_CHANGED_SIGNAL,
info->imsi);
}
static void ril_sim_info_dbus_spn_cb(struct ril_sim_info *info, void *arg)
{
struct ril_sim_info_dbus *dbus = arg;
ril_sim_info_dbus_emit(dbus, RIL_SIM_INFO_DBUS_SPN_CHANGED_SIGNAL,
info->spn);
}
struct ril_sim_info_dbus *ril_sim_info_dbus_new(struct ril_modem *md,
struct ril_sim_info *info)
{
struct ril_sim_info_dbus *dbus = g_new0(struct ril_sim_info_dbus, 1);
DBG("%s", ril_modem_get_path(md));
dbus->md = md;
dbus->path = g_strdup(ril_modem_get_path(md));
dbus->info = ril_sim_info_ref(info);
dbus->conn = dbus_connection_ref(ofono_dbus_get_connection());
/* Register D-Bus interface */
if (g_dbus_register_interface(dbus->conn, dbus->path,
RIL_SIM_INFO_DBUS_INTERFACE, ril_sim_info_dbus_methods,
ril_sim_info_dbus_signals, NULL, dbus, NULL)) {
ofono_modem_add_interface(md->ofono,
RIL_SIM_INFO_DBUS_INTERFACE);
dbus->handler_id[SIM_INFO_EVENT_ICCID] =
ril_sim_info_add_iccid_changed_handler(info,
ril_sim_info_dbus_iccid_cb, dbus);
dbus->handler_id[SIM_INFO_EVENT_IMSI] =
ril_sim_info_add_imsi_changed_handler(info,
ril_sim_info_dbus_imsi_cb, dbus);
dbus->handler_id[SIM_INFO_EVENT_SPN] =
ril_sim_info_add_spn_changed_handler(info,
ril_sim_info_dbus_spn_cb, dbus);
return dbus;
} else {
ofono_error("CellInfo D-Bus register failed");
ril_sim_info_dbus_free(dbus);
return NULL;
}
}
void ril_sim_info_dbus_free(struct ril_sim_info_dbus *dbus)
{
if (dbus) {
unsigned int i;
DBG("%s", dbus->path);
g_dbus_unregister_interface(dbus->conn, dbus->path,
RIL_SIM_INFO_DBUS_INTERFACE);
ofono_modem_remove_interface(dbus->md->ofono,
RIL_SIM_INFO_DBUS_INTERFACE);
dbus_connection_unref(dbus->conn);
for (i=0; i<G_N_ELEMENTS(dbus->handler_id); i++) {
ril_sim_info_remove_handler(dbus->info,
dbus->handler_id[i]);
}
ril_sim_info_unref(dbus->info);
g_free(dbus->path);
g_free(dbus);
}
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2016 Jolla Ltd.
* Copyright (C) 2016-2017 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
@@ -16,26 +16,31 @@
#include "ril_sim_settings.h"
#include "ril_log.h"
#include "sailfish_watch.h"
#include <gutil_misc.h>
#include <ofono/sim.h>
#include "ofono.h"
#include "storage.h"
#define RIL_SIM_STORE "ril"
#define RIL_SIM_STORE_GROUP "Settings"
#define RIL_SIM_STORE_PREF_MODE "TechnologyPreference"
#define RIL_SIM_STORE_PREF_MODE_DEFAULT(self) ((self)->enable_4g ? \
OFONO_RADIO_ACCESS_MODE_LTE : OFONO_RADIO_ACCESS_MODE_UMTS)
#define RIL_PREF_MODE_DEFAULT(self) (\
((self)->techs & OFONO_RADIO_ACCESS_MODE_LTE) ? \
OFONO_RADIO_ACCESS_MODE_LTE : \
((self)->techs & OFONO_RADIO_ACCESS_MODE_UMTS) ? \
OFONO_RADIO_ACCESS_MODE_UMTS : \
OFONO_RADIO_ACCESS_MODE_GSM)
typedef GObjectClass RilSimSettingsClass;
typedef struct ril_sim_settings RilSimSettings;
enum sailfish_watch_events {
WATCH_EVENT_IMSI,
WATCH_EVENT_COUNT
};
struct ril_sim_settings_priv {
struct ofono_sim *sim;
guint imsi_watch_id;
guint state_watch_id;
gulong watch_event_id[WATCH_EVENT_COUNT];
struct sailfish_watch *watch;
GKeyFile *storage;
char *imsi;
};
@@ -62,176 +67,77 @@ G_DEFINE_TYPE(RilSimSettings, ril_sim_settings, G_TYPE_OBJECT)
G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST, \
0, NULL, NULL, NULL, G_TYPE_NONE, 0)
/* Skip the leading slash from the modem path: */
#define DBG_(obj,fmt,args...) DBG("%s " fmt, (obj)->path+1, ##args)
static void ril_sim_settings_signal_emit(struct ril_sim_settings *self,
enum ril_sim_settings_signal id)
{
g_signal_emit(self, ril_sim_settings_signals[id], 0);
}
static void ril_sim_settings_reload(struct ril_sim_settings *self)
{
struct ril_sim_settings_priv *priv = self->priv;
if (priv->storage) {
g_key_file_free(priv->storage);
priv->storage = NULL;
}
if (priv->imsi) {
char *mode_str;
enum ofono_radio_access_mode mode;
priv->storage = storage_open(priv->imsi, RIL_SIM_STORE);
mode_str = g_key_file_get_string(priv->storage,
RIL_SIM_STORE_GROUP, RIL_SIM_STORE_PREF_MODE, NULL);
if (ofono_radio_access_mode_from_string(mode_str, &mode)) {
if (!self->enable_4g &&
mode == OFONO_RADIO_ACCESS_MODE_LTE) {
mode = OFONO_RADIO_ACCESS_MODE_ANY;
}
} else {
mode = OFONO_RADIO_ACCESS_MODE_ANY;
}
if (mode == OFONO_RADIO_ACCESS_MODE_ANY) {
self->pref_mode = RIL_SIM_STORE_PREF_MODE_DEFAULT(self);
} else {
self->pref_mode = mode;
}
g_free(mode_str);
}
}
void ril_sim_settings_set_pref_mode(struct ril_sim_settings *self,
enum ofono_radio_access_mode mode)
{
if (G_LIKELY(self) && self->pref_mode != mode) {
struct ril_sim_settings_priv *priv = self->priv;
const char *mode_str = ofono_radio_access_mode_to_string(mode);
GASSERT(priv->storage);
if (mode_str) {
if (priv->storage) {
g_key_file_set_string(priv->storage,
RIL_SIM_STORE_GROUP,
RIL_SIM_STORE_PREF_MODE, mode_str);
storage_sync(self->imsi, RIL_SIM_STORE,
priv->storage);
}
self->pref_mode = mode;
ril_sim_settings_signal_emit(self,
SIGNAL_PREF_MODE_CHANGED);
}
self->pref_mode = mode;
ril_sim_settings_signal_emit(self, SIGNAL_PREF_MODE_CHANGED);
}
}
static void ril_sim_settings_set_imsi(struct ril_sim_settings *self,
const char *imsi)
{
struct ril_sim_settings_priv *priv = self->priv;
if (g_strcmp0(priv->imsi, imsi)) {
enum ofono_radio_access_mode prev_mode = self->pref_mode;
g_free(priv->imsi);
self->imsi = priv->imsi = g_strdup(imsi);
ril_sim_settings_reload(self);
ril_sim_settings_signal_emit(self, SIGNAL_IMSI_CHANGED);
if (prev_mode != self->pref_mode) {
ril_sim_settings_signal_emit(self,
SIGNAL_PREF_MODE_CHANGED);
}
}
}
static void ril_sim_settings_imsi_watch_cb(const char *imsi, void *user_data)
{
ril_sim_settings_set_imsi(RIL_SIM_SETTINGS(user_data), imsi);
}
static void ril_sim_settings_imsi_watch_done(void *user_data)
{
struct ril_sim_settings *self = RIL_SIM_SETTINGS(user_data);
struct ril_sim_settings_priv *priv = self->priv;
GASSERT(priv->imsi_watch_id);
priv->imsi_watch_id = 0;
}
static void ril_sim_settings_state_check(struct ril_sim_settings *self,
enum ofono_sim_state new_state)
{
if (new_state != OFONO_SIM_STATE_READY) {
ril_sim_settings_set_imsi(self, NULL);
}
}
static void ril_sim_settings_state_watch(enum ofono_sim_state new_state,
static void ril_sim_settings_imsi_changed(struct sailfish_watch *watch,
void *user_data)
{
ril_sim_settings_state_check(RIL_SIM_SETTINGS(user_data), new_state);
}
static void ril_sim_settings_state_watch_done(void *user_data)
{
struct ril_sim_settings *self = RIL_SIM_SETTINGS(user_data);
struct ril_sim_settings_priv *priv = self->priv;
GASSERT(priv->state_watch_id);
priv->state_watch_id = 0;
}
void ril_sim_settings_set_ofono_sim(struct ril_sim_settings *self,
struct ofono_sim *sim)
{
if (G_LIKELY(self)) {
struct ril_sim_settings_priv *priv = self->priv;
if (priv->sim != sim) {
GASSERT(priv->sim || !priv->imsi_watch_id);
if (priv->imsi_watch_id) {
ofono_sim_remove_imsi_watch(priv->sim,
priv->imsi_watch_id);
/*
* ril_sim_settings_imsi_watch_done
* clears it
*/
GASSERT(!priv->imsi_watch_id);
}
if (priv->state_watch_id) {
ofono_sim_remove_state_watch(priv->sim,
priv->state_watch_id);
/*
* ril_sim_settings_state_watch_done
* clears it
*/
GASSERT(!priv->state_watch_id);
}
priv->sim = sim;
if (sim) {
priv->state_watch_id =
ofono_sim_add_state_watch(sim,
ril_sim_settings_state_watch, self,
ril_sim_settings_state_watch_done);
GASSERT(priv->state_watch_id);
ril_sim_settings_state_check(self,
ofono_sim_get_state(sim));
/*
* ofono_sim_add_imsi_watch immediately
* calls the event callback if IMSI is
* already known. It's useless though
* because we still have to check the
* current state in case if IMSI is not
* available yet.
*/
priv->imsi_watch_id =
ofono_sim_add_imsi_watch(priv->sim,
ril_sim_settings_imsi_watch_cb, self,
ril_sim_settings_imsi_watch_done);
GASSERT(priv->state_watch_id);
}
/* Luckily, ofono_sim_get_imsi handles NULL pointer */
ril_sim_settings_set_imsi(self,
ofono_sim_get_imsi(sim));
}
if (g_strcmp0(priv->imsi, watch->imsi)) {
g_free(priv->imsi);
self->imsi = priv->imsi = g_strdup(watch->imsi);
ril_sim_settings_signal_emit(self, SIGNAL_IMSI_CHANGED);
}
}
struct ril_sim_settings *ril_sim_settings_new(const char *path,
enum ofono_radio_access_mode techs)
{
struct ril_sim_settings *self = NULL;
if (G_LIKELY(path)) {
struct ril_sim_settings_priv *priv;
self = g_object_new(RIL_SIM_SETTINGS_TYPE, NULL);
priv = self->priv;
self->techs = techs;
self->pref_mode = RIL_PREF_MODE_DEFAULT(self);
priv->watch = sailfish_watch_new(path);
priv->watch_event_id[WATCH_EVENT_IMSI] =
sailfish_watch_add_imsi_changed_handler(priv->watch,
ril_sim_settings_imsi_changed, self);
self->imsi = priv->imsi = g_strdup(priv->watch->imsi);
}
return self;
}
struct ril_sim_settings *ril_sim_settings_ref(struct ril_sim_settings *self)
{
if (G_LIKELY(self)) {
g_object_ref(RIL_SIM_SETTINGS(self));
return self;
} else {
return NULL;
}
}
void ril_sim_settings_unref(struct ril_sim_settings *self)
{
if (G_LIKELY(self)) {
g_object_unref(RIL_SIM_SETTINGS(self));
}
}
gulong ril_sim_settings_add_imsi_changed_handler(struct ril_sim_settings *self,
ril_sim_settings_cb_t cb, void *arg)
{
@@ -260,51 +166,26 @@ void ril_sim_settings_remove_handlers(struct ril_sim_settings *self,
gutil_disconnect_handlers(self, ids, count);
}
struct ril_sim_settings *ril_sim_settings_new(const struct ril_slot_config *sc)
{
struct ril_sim_settings *self = g_object_new(RIL_SIM_SETTINGS_TYPE, 0);
self->enable_4g = sc->enable_4g;
self->slot = sc->slot;
self->pref_mode = RIL_SIM_STORE_PREF_MODE_DEFAULT(self);
return self;
}
struct ril_sim_settings *ril_sim_settings_ref(struct ril_sim_settings *self)
{
if (G_LIKELY(self)) {
g_object_ref(RIL_SIM_SETTINGS(self));
return self;
} else {
return NULL;
}
}
void ril_sim_settings_unref(struct ril_sim_settings *self)
{
if (G_LIKELY(self)) {
g_object_unref(RIL_SIM_SETTINGS(self));
}
}
static void ril_sim_settings_init(struct ril_sim_settings *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, RIL_SIM_SETTINGS_TYPE,
struct ril_sim_settings_priv);
}
static void ril_sim_settings_dispose(GObject *object)
static void ril_sim_settings_finalize(GObject *object)
{
struct ril_sim_settings *self = RIL_SIM_SETTINGS(object);
struct ril_sim_settings_priv *priv = self->priv;
ril_sim_settings_set_ofono_sim(self, NULL);
G_OBJECT_CLASS(ril_sim_settings_parent_class)->dispose(object);
sailfish_watch_remove_all_handlers(priv->watch, priv->watch_event_id);
sailfish_watch_unref(priv->watch);
g_free(priv->imsi);
G_OBJECT_CLASS(ril_sim_settings_parent_class)->finalize(object);
}
static void ril_sim_settings_class_init(RilSimSettingsClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS(klass);
object_class->dispose = ril_sim_settings_dispose;
G_OBJECT_CLASS(klass)->finalize = ril_sim_settings_finalize;
g_type_class_add_private(klass, sizeof(struct ril_sim_settings_priv));
NEW_SIGNAL(klass, IMSI);
NEW_SIGNAL(klass, PREF_MODE);

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2016 Jolla Ltd.
* Copyright (C) 2016-2017 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
@@ -18,22 +18,20 @@
#include "ril_types.h"
#include <ofono/radio-settings.h>
struct ril_sim_settings_priv;
struct ril_sim_settings {
GObject object;
struct ril_sim_settings_priv *priv;
gboolean enable_4g;
guint slot;
const char *imsi;
enum ofono_radio_access_mode techs;
enum ofono_radio_access_mode pref_mode;
};
typedef void (*ril_sim_settings_cb_t)(struct ril_sim_settings *s, void *arg);
struct ril_sim_settings *ril_sim_settings_new(const struct ril_slot_config *sc);
struct ril_sim_settings *ril_sim_settings_new(const char *path,
enum ofono_radio_access_mode techs);
struct ril_sim_settings *ril_sim_settings_ref(struct ril_sim_settings *s);
void ril_sim_settings_unref(struct ril_sim_settings *s);
void ril_sim_settings_set_ofono_sim(struct ril_sim_settings *s,

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2016 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -14,7 +14,6 @@
*/
#include "ril_plugin.h"
#include "ril_constants.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 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -16,7 +16,6 @@
#include "ril_plugin.h"
#include "ril_util.h"
#include "ril_log.h"
#include "ril_constants.h"
#include "util.h"

View File

@@ -35,6 +35,17 @@
#
#3GLTEHandover=true
# RIL_REQUEST_SET_RADIO_CAPABILITY may or may not be supported by your RIL.
# This option allows you to forcibly enable or disable use of this request.
# It's involved in 3G/LTE handover between the modems, meaning that it only
# makes sense if you have more than one slot.
#
# Possible values are auto, on and off
#
# Default is auto (enable for RIL version >= 11)
#
#SetRadioCapability=auto
[ril_0]
# Required entry, defines the RIL socket path
@@ -67,9 +78,15 @@ socket=/dev/socket/rild
#
#timeout=0
# Setting this one to false would disable 4G technology selection.
# Comma-separated list of radio technologies supported by the modem.
# Valid technologies are "gsm", "umts" and "lte". The special value
# "all" means that all technologies are supported.
#
# By default 4G is enabled
# The default is all
#
#technologies=all
# This one is deprecated, use the technologies entry instead (above).
#
#enable4G=true
@@ -100,7 +117,7 @@ socket=/dev/socket/rild
# This option allows you to forcibly enable or disable use of this request.
# Possible values are auto, on and off
#
# Default is auto (usage based on the RIL version)
# Default is auto (enable for RIL version >= 11)
#
#allowDataReq=auto
@@ -142,3 +159,20 @@ socket=/dev/socket/rild
# Default is 200 ms
#
#dataCallRetryDelay=200
# Additional local and remote hangup reasons. Remote reasons are checked
# first. Normally, RIL plugin figures it out automatically. You would only
# need to define these if your RIL does something unusual.
#
# No default
#
#remoteHangupReasons=20
#localHangupReasons=23
# Voice call support. Some devices like USB modems and tablets don't support
# voice calls. By default, voice calls are enabled and this option allows you
# to disable voice call handling.
#
# Default true
#
#enableVoicecall=true

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2016 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -23,11 +23,12 @@
#include <grilio_types.h>
#include <gutil_macros.h>
#include <ofono/types.h>
struct ofono_modem;
struct ofono_sim;
#include <ofono/types.h>
#include <ofono/radio-settings.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
@@ -39,20 +40,20 @@ struct ofono_sim;
#define RIL_RETRY_SECS (2)
#define RIL_RETRY_MS (RIL_RETRY_SECS*1000)
struct ril_mce;
struct ril_data;
struct ril_modem;
struct ril_radio;
struct ril_network;
struct ril_sim_card;
struct ril_sim_info;
struct ril_sim_settings;
struct ril_cell_info;
struct ril_slot_config {
guint slot;
gboolean enable_4g;
enum ofono_radio_access_mode techs;
gboolean empty_pin_query;
gboolean enable_voicecall;
GUtilInts *local_hangup_reasons;
GUtilInts *remote_hangup_reasons;
};
#endif /* RIL_TYPES_H */

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2016 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -16,7 +16,6 @@
#include "ril_plugin.h"
#include "ril_util.h"
#include "ril_log.h"
#include "ril_constants.h"
#include "smsutil.h"
#include "util.h"

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2016 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -48,7 +48,8 @@ const char *ril_error_to_string(int error)
RIL_E_(MODE_NOT_SUPPORTED);
RIL_E_(FDN_CHECK_FAILURE);
RIL_E_(ILLEGAL_SIM_OR_ME);
RIL_E_(UNUSED);
RIL_E_(MISSING_RESOURCE);
RIL_E_(NO_SUCH_ELEMENT);
RIL_E_(DIAL_MODIFIED_TO_USSD);
RIL_E_(DIAL_MODIFIED_TO_SS);
RIL_E_(DIAL_MODIFIED_TO_DIAL);
@@ -57,11 +58,39 @@ const char *ril_error_to_string(int error)
RIL_E_(USSD_MODIFIED_TO_USSD);
RIL_E_(SS_MODIFIED_TO_DIAL);
RIL_E_(SS_MODIFIED_TO_USSD);
RIL_E_(SS_MODIFIED_TO_SS);
RIL_E_(SUBSCRIPTION_NOT_SUPPORTED);
RIL_E_(MISSING_RESOURCE);
RIL_E_(NO_SUCH_ELEMENT);
RIL_E_(INVALID_PARAMETER);
RIL_E_(SS_MODIFIED_TO_SS);
RIL_E_(LCE_NOT_SUPPORTED);
RIL_E_(NO_MEMORY);
RIL_E_(INTERNAL_ERR);
RIL_E_(SYSTEM_ERR);
RIL_E_(MODEM_ERR);
RIL_E_(INVALID_STATE);
RIL_E_(NO_RESOURCES);
RIL_E_(SIM_ERR);
RIL_E_(INVALID_ARGUMENTS);
RIL_E_(INVALID_SIM_STATE);
RIL_E_(INVALID_MODEM_STATE);
RIL_E_(INVALID_CALL_ID);
RIL_E_(NO_SMS_TO_ACK);
RIL_E_(NETWORK_ERR);
RIL_E_(REQUEST_RATE_LIMITED);
RIL_E_(SIM_BUSY);
RIL_E_(SIM_FULL);
RIL_E_(NETWORK_REJECT);
RIL_E_(OPERATION_NOT_ALLOWED);
RIL_E_(EMPTY_RECORD);
RIL_E_(INVALID_SMS_FORMAT);
RIL_E_(ENCODING_ERR);
RIL_E_(INVALID_SMSC_ADDRESS);
RIL_E_(NO_SUCH_ENTRY);
RIL_E_(NETWORK_NOT_READY);
RIL_E_(NOT_PROVISIONED);
RIL_E_(NO_SUBSCRIPTION);
RIL_E_(NO_NETWORK_FOUND);
RIL_E_(DEVICE_IN_USE);
RIL_E_(ABORTED);
RIL_E_(INVALID_RESPONSE);
default:
snprintf(unknown, sizeof(unknown), "%d", error);
return unknown;

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* Copyright (C) 2015-2016 Jolla Ltd.
* Copyright (C) 2015-2017 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
@@ -14,17 +14,21 @@
*/
#include "ril_plugin.h"
#include "ril_constants.h"
#include "ril_ecclist.h"
#include "ril_util.h"
#include "ril_log.h"
#include "common.h"
#include <gutil_ints.h>
#include <gutil_ring.h>
#include <gutil_idlequeue.h>
#include <gutil_intarray.h>
#define FLAG_NEED_CLIP 1
#define VOICECALL_BLOCK_TIMEOUT_MS (5*1000)
enum ril_voicecall_events {
VOICECALL_EVENT_CALL_STATE_CHANGED,
VOICECALL_EVENT_SUPP_SVC_NOTIFICATION,
@@ -38,12 +42,14 @@ struct ril_voicecall {
GRilIoQueue *q;
struct ofono_voicecall *vc;
struct ril_ecclist *ecclist;
unsigned int local_release;
unsigned char flags;
ofono_voicecall_cb_t cb;
void *data;
guint timer_id;
GUtilRing* dtmf_queue;
GUtilIntArray *local_release_ids;
GUtilIdleQueue *idleq;
GUtilRing *dtmf_queue;
GUtilInts *local_hangup_reasons;
GUtilInts *remote_hangup_reasons;
guint send_dtmf_id;
guint clcc_poll_id;
gulong event_id[VOICECALL_EVENT_COUNT];
@@ -52,35 +58,48 @@ struct ril_voicecall {
gulong ecclist_change_id;
};
struct ril_voicecall_change_state_req {
struct ril_voicecall_request_data {
int ref_count;
int pending_call_count;
int success;
struct ofono_voicecall *vc;
ofono_voicecall_cb_t cb;
gpointer data;
int affected_types;
};
struct lastcause_req {
struct ofono_voicecall *vc;
struct ril_voicecall *vd;
int id;
};
static void ril_voicecall_send_one_dtmf(struct ril_voicecall *vd);
static void ril_voicecall_clear_dtmf_queue(struct ril_voicecall *vd);
/*
* structs ofono_voicecall and voicecall are fully defined
* in src/voicecall.c; we need (read) access to the
* call objects, so partially redefine them here.
*/
struct ofono_voicecall {
GSList *call_list;
/* ... */
};
struct ril_voicecall_request_data *ril_voicecall_request_data_new
(struct ofono_voicecall *vc, ofono_voicecall_cb_t cb, void *data)
{
struct ril_voicecall_request_data *req =
g_slice_new0(struct ril_voicecall_request_data);
struct voicecall {
struct ofono_call *call;
/* ... */
};
req->ref_count = 1;
req->vc = vc;
req->cb = cb;
req->data = data;
return req;
}
static void ril_voicecall_request_data_unref
(struct ril_voicecall_request_data *req)
{
if (!--req->ref_count) {
g_slice_free(struct ril_voicecall_request_data, req);
}
}
static void ril_voicecall_request_data_free(gpointer data)
{
ril_voicecall_request_data_unref(data);
}
static inline struct ril_voicecall *ril_voicecall_get_data(
struct ofono_voicecall *vc)
@@ -166,38 +185,76 @@ static GSList *ril_voicecall_parse_clcc(const void *data, guint len)
}
/* Valid call statuses have value >= 0 */
static int call_status_with_id(struct ofono_voicecall *vc, int id)
static int ril_voicecall_status_with_id(struct ofono_voicecall *vc,
unsigned int id)
{
GSList *l;
struct voicecall *v;
struct ofono_call *call = ofono_voicecall_find_call(vc, id);
GASSERT(vc);
return call ? call->status : -1;
}
for (l = vc->call_list; l; l = l->next) {
v = l->data;
if (v->call->id == id) {
return v->call->status;
/* Tries to parse the payload as a uint followed by a string */
static int ril_voicecall_parse_lastcause_1(const void *data, guint len)
{
int result = -1;
if (len > 8) {
int code;
char *msg = NULL;
GRilIoParser rilp;
grilio_parser_init(&rilp, data, len);
if (grilio_parser_get_int32(&rilp, &code) && code >= 0 &&
(msg = grilio_parser_get_utf8(&rilp)) &&
grilio_parser_at_end(&rilp)) {
DBG("%d \"%s\"", code, msg);
result = code;
}
g_free(msg);
}
return -1;
return result;
}
static void ril_voicecall_lastcause_cb(GRilIoChannel *io, int status,
const void *data, guint len, void *user_data)
{
struct lastcause_req *reqdata = user_data;
struct ofono_voicecall *vc = reqdata->vc;
int tmp;
struct ril_voicecall *vd = reqdata->vd;
struct ofono_voicecall *vc = vd->vc;
int id = reqdata->id;
int call_status;
enum ofono_disconnect_reason reason = OFONO_DISCONNECT_REASON_ERROR;
int last_cause = CALL_FAIL_ERROR_UNSPECIFIED;
GRilIoParser rilp;
grilio_parser_init(&rilp, data, len);
if (grilio_parser_get_int32(&rilp, &tmp) && tmp > 0) {
grilio_parser_get_int32(&rilp, &last_cause);
int last_cause;
/*
* According to ril.h:
*
* "response" is a "int *"
* ((int *)response)[0] is RIL_LastCallFailCause. GSM failure
* reasons are mapped to cause codes defined in TS 24.008 Annex H
* where possible.
*
* However some RILs feel free to invent their own formats,
* try those first.
*/
last_cause = ril_voicecall_parse_lastcause_1(data, len);
if (last_cause < 0) {
GRilIoParser rilp;
int num, code;
/* Default format described in ril.h */
grilio_parser_init(&rilp, data, len);
if (grilio_parser_get_int32(&rilp, &num) && num == 1 &&
grilio_parser_get_int32(&rilp, &code) &&
grilio_parser_at_end(&rilp)) {
last_cause = code;
} else {
ofono_warn("Unable to parse last call fail cause");
last_cause = CALL_FAIL_ERROR_UNSPECIFIED;
}
}
/*
@@ -208,7 +265,14 @@ static void ril_voicecall_lastcause_cb(GRilIoChannel *io, int status,
* CALL_FAIL_ERROR_UNSPECIFIED, and thus indistinguishable
* from a network failure.
*/
switch (last_cause) {
if (gutil_ints_contains(vd->remote_hangup_reasons, last_cause)) {
DBG("hangup cause %d => remote hangup", last_cause);
reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP;
} else if (gutil_ints_contains(vd->local_hangup_reasons, last_cause)) {
DBG("hangup cause %d => local hangup", last_cause);
reason = OFONO_DISCONNECT_REASON_LOCAL_HANGUP;
} else {
switch (last_cause) {
case CALL_FAIL_UNOBTAINABLE_NUMBER:
case CALL_FAIL_NORMAL:
case CALL_FAIL_BUSY:
@@ -216,19 +280,19 @@ static void ril_voicecall_lastcause_cb(GRilIoChannel *io, int status,
case CALL_FAIL_CHANNEL_UNACCEPTABLE:
case CALL_FAIL_OPERATOR_DETERMINED_BARRING:
case CALL_FAIL_NO_USER_RESPONDING:
case CALL_FAIL_USER_ALERTING_NO_ANSWER:
case CALL_FAIL_NO_ANSWER_FROM_USER:
case CALL_FAIL_CALL_REJECTED:
case CALL_FAIL_NUMBER_CHANGED:
case CALL_FAIL_ANONYMOUS_CALL_REJECTION:
case CALL_FAIL_PRE_EMPTION:
case CALL_FAIL_DESTINATION_OUT_OF_ORDER:
case CALL_FAIL_INCOMPLETE_NUMBER:
case CALL_FAIL_INVALID_NUMBER_FORMAT:
case CALL_FAIL_FACILITY_REJECTED:
reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP;
break;
case CALL_FAIL_NORMAL_UNSPECIFIED:
call_status = call_status_with_id(vc, id);
call_status = ril_voicecall_status_with_id(vc, id);
if (call_status == CALL_STATUS_ACTIVE ||
call_status == CALL_STATUS_HELD ||
call_status == CALL_STATUS_DIALING ||
@@ -240,7 +304,7 @@ static void ril_voicecall_lastcause_cb(GRilIoChannel *io, int status,
break;
case CALL_FAIL_ERROR_UNSPECIFIED:
call_status = call_status_with_id(vc, id);
call_status = ril_voicecall_status_with_id(vc, id);
if (call_status == CALL_STATUS_DIALING ||
call_status == CALL_STATUS_ALERTING) {
reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP;
@@ -250,6 +314,7 @@ static void ril_voicecall_lastcause_cb(GRilIoChannel *io, int status,
default:
reason = OFONO_DISCONNECT_REASON_ERROR;
break;
}
}
ofono_info("Call %d ended with RIL cause %d -> ofono reason %d",
@@ -285,7 +350,9 @@ static void ril_voicecall_clcc_poll_cb(GRilIoChannel *io, int status,
struct ofono_call *oc = o ? o->data : NULL;
if (oc && (nc == NULL || (nc->id > oc->id))) {
if (vd->local_release & (1 << oc->id)) {
/* old call is gone */
if (gutil_int_array_remove_all_fast(
vd->local_release_ids, oc->id)) {
ofono_voicecall_disconnected(vd->vc, oc->id,
OFONO_DISCONNECT_REASON_LOCAL_HANGUP,
NULL);
@@ -295,7 +362,7 @@ static void ril_voicecall_clcc_poll_cb(GRilIoChannel *io, int status,
struct lastcause_req *reqdata =
g_new0(struct lastcause_req, 1);
reqdata->vc = vd->vc;
reqdata->vd = vd;
reqdata->id = oc->id;
grilio_queue_send_request_full(vd->q, NULL,
RIL_REQUEST_LAST_CALL_FAIL_CAUSE,
@@ -365,9 +432,7 @@ static void ril_voicecall_clcc_poll_cb(GRilIoChannel *io, int status,
}
g_slist_free_full(vd->calls, g_free);
vd->calls = calls;
vd->local_release = 0;
}
static void ril_voicecall_clcc_poll(struct ril_voicecall *vd)
@@ -386,52 +451,47 @@ static void ril_voicecall_clcc_poll(struct ril_voicecall *vd)
static void ril_voicecall_request_cb(GRilIoChannel *io, int status,
const void *data, guint len, void *user_data)
{
struct ril_voicecall_change_state_req *req = user_data;
struct ril_voicecall_request_data *req = user_data;
struct ril_voicecall *vd = ril_voicecall_get_data(req->vc);
struct ofono_error error;
if (status == RIL_E_SUCCESS) {
GSList *l;
if (req->affected_types) {
for (l = vd->calls; l; l = l->next) {
struct ofono_call *call = l->data;
if (req->affected_types & (1 << call->status)) {
vd->local_release |= (1 << call->id);
}
}
}
ril_error_init_ok(&error);
} else {
ofono_error("generic fail");
ril_error_init_failure(&error);
}
ril_voicecall_clcc_poll(vd);
/* We have to callback after we schedule a poll if required */
if (req->cb) {
/*
* The ofono API call is considered successful if at least one
* associated RIL request succeeds.
*/
if (status == RIL_E_SUCCESS) {
req->success++;
}
/*
* Only invoke the callback if this is the last request associated
* with this ofono api call (pending call count becomes zero).
*/
GASSERT(req->pending_call_count > 0);
if (!--req->pending_call_count && req->cb) {
struct ofono_error error;
if (req->success) {
ril_error_init_ok(&error);
} else {
ril_error_init_failure(&error);
}
req->cb(&error, req->data);
}
}
static void ril_voicecall_request(const guint rreq, struct ofono_voicecall *vc,
unsigned int affected_types, GRilIoRequest *ioreq,
ofono_voicecall_cb_t cb, void *data)
static void ril_voicecall_request(const guint code, struct ofono_voicecall *vc,
GRilIoRequest *req, ofono_voicecall_cb_t cb, void *data)
{
struct ril_voicecall *vd = ril_voicecall_get_data(vc);
struct ril_voicecall_change_state_req *req;
struct ril_voicecall_request_data *req_data =
ril_voicecall_request_data_new(vc, cb, data);
req = g_new0(struct ril_voicecall_change_state_req, 1);
req->vc = vc;
req->cb = cb;
req->data = data;
req->affected_types = affected_types;
grilio_queue_send_request_full(vd->q, ioreq, rreq,
ril_voicecall_request_cb, g_free, req);
req_data->pending_call_count++;
grilio_queue_send_request_full(ril_voicecall_get_data(vc)->q, req,
code, ril_voicecall_request_cb,
ril_voicecall_request_data_free, req_data);
}
static void ril_voicecall_dial_cb(GRilIoChannel *io, int status,
@@ -489,47 +549,68 @@ static void ril_voicecall_dial(struct ofono_voicecall *vc,
grilio_request_unref(req);
}
static void ril_voicecall_submit_hangup_req(struct ofono_voicecall *vc,
int id, struct ril_voicecall_request_data *req)
{
struct ril_voicecall *vd = ril_voicecall_get_data(vc);
GRilIoRequest *ioreq = grilio_request_array_int32_new(1, id);
/* Append the call id to the list of calls being released locally */
GASSERT(!gutil_int_array_contains(vd->local_release_ids, id));
gutil_int_array_append(vd->local_release_ids, id);
/* Send request to RIL. ril_voicecall_request_data_free will unref
* the request data */
req->ref_count++;
req->pending_call_count++;
grilio_queue_send_request_full(vd->q, ioreq, RIL_REQUEST_HANGUP,
ril_voicecall_request_cb,
ril_voicecall_request_data_free, req);
grilio_request_unref(ioreq);
}
static void ril_voicecall_hangup_all(struct ofono_voicecall *vc,
ofono_voicecall_cb_t cb, void *data)
{
struct ril_voicecall *vd = ril_voicecall_get_data(vc);
struct ofono_error error;
GSList *l;
for (l = vd->calls; l; l = l->next) {
struct ofono_call *call = l->data;
GRilIoRequest *req = grilio_request_sized_new(8);
if (vd->calls) {
GSList *l;
struct ril_voicecall_request_data *req =
ril_voicecall_request_data_new(vc, cb, data);
/* TODO: Hangup just the active ones once we have call
* state tracking (otherwise it can't handle ringing) */
DBG("Hanging up call with id %d", call->id);
grilio_request_append_int32(req, 1); /* Always 1 - AT+CHLD=1x */
grilio_request_append_int32(req, call->id);
/*
* Here the idea is that we submit (potentially) multiple
* hangup requests to RIL and invoke the callback after
* the last request has completed (pending call count
* becomes zero).
*/
for (l = vd->calls; l; l = l->next) {
struct ofono_call *call = l->data;
/* Send request to RIL */
ril_voicecall_request(RIL_REQUEST_HANGUP, vc, 0x3f, req,
NULL, NULL);
grilio_request_unref(req);
/* Send request to RIL */
DBG("Hanging up call with id %d", call->id);
ril_voicecall_submit_hangup_req(vc, call->id, req);
}
/* Release our reference */
ril_voicecall_request_data_unref(req);
} else {
/* No calls */
struct ofono_error error;
cb(ril_error_ok(&error), data);
}
/* TODO: Deal in case of an error at hungup */
cb(ril_error_ok(&error), data);
}
static void ril_voicecall_hangup_specific(struct ofono_voicecall *vc,
static void ril_voicecall_release_specific(struct ofono_voicecall *vc,
int id, ofono_voicecall_cb_t cb, void *data)
{
GRilIoRequest *req = grilio_request_sized_new(8);
struct ofono_error error;
struct ril_voicecall_request_data *req =
ril_voicecall_request_data_new(vc, cb, data);
DBG("Hanging up call with id %d", id);
grilio_request_append_int32(req, 1); /* Always 1 - AT+CHLD=1x */
grilio_request_append_int32(req, id);
/* Send request to RIL */
ril_voicecall_request(RIL_REQUEST_HANGUP, vc, 0x3f, req, NULL, NULL);
grilio_request_unref(req);
cb(ril_error_ok(&error), data);
ril_voicecall_submit_hangup_req(vc, id, req);
ril_voicecall_request_data_unref(req);
}
static void ril_voicecall_call_state_changed_event(GRilIoChannel *io,
@@ -584,7 +665,7 @@ static void ril_voicecall_answer(struct ofono_voicecall *vc,
{
/* Send request to RIL */
DBG("Answering current call");
ril_voicecall_request(RIL_REQUEST_ANSWER, vc, 0, NULL, cb, data);
ril_voicecall_request(RIL_REQUEST_ANSWER, vc, NULL, cb, data);
}
static void ril_voicecall_send_dtmf_cb(GRilIoChannel *io, int status,
@@ -653,29 +734,25 @@ static void ril_voicecall_clear_dtmf_queue(struct ril_voicecall *vd)
static void ril_voicecall_create_multiparty(struct ofono_voicecall *vc,
ofono_voicecall_cb_t cb, void *data)
{
ril_voicecall_request(RIL_REQUEST_CONFERENCE,
vc, 0, NULL, cb, data);
ril_voicecall_request(RIL_REQUEST_CONFERENCE, vc, NULL, cb, data);
}
static void ril_voicecall_transfer(struct ofono_voicecall *vc,
ofono_voicecall_cb_t cb, void *data)
{
ril_voicecall_request(RIL_REQUEST_EXPLICIT_CALL_TRANSFER,
vc, 0, NULL, cb, data);
vc, NULL, cb, data);
}
static void ril_voicecall_private_chat(struct ofono_voicecall *vc, int id,
ofono_voicecall_cb_t cb, void *data)
{
GRilIoRequest *req = grilio_request_sized_new(8);
GRilIoRequest *req = grilio_request_array_int32_new(1, id);
struct ofono_error error;
DBG("Private chat with id %d", id);
grilio_request_append_int32(req, 1);
grilio_request_append_int32(req, id);
ril_voicecall_request(RIL_REQUEST_SEPARATE_CONNECTION,
vc, 0, req, NULL, NULL);
vc, req, NULL, NULL);
grilio_request_unref(req);
cb(ril_error_ok(&error), data);
}
@@ -683,51 +760,52 @@ static void ril_voicecall_private_chat(struct ofono_voicecall *vc, int id,
static void ril_voicecall_swap_without_accept(struct ofono_voicecall *vc,
ofono_voicecall_cb_t cb, void *data)
{
DBG("");
ril_voicecall_request(RIL_REQUEST_SWITCH_HOLDING_AND_ACTIVE,
vc, 0, NULL, cb, data);
vc, NULL, cb, data);
}
static void ril_voicecall_hold_all_active(struct ofono_voicecall *vc,
ofono_voicecall_cb_t cb, void *data)
{
DBG("");
ril_voicecall_request(RIL_REQUEST_SWITCH_HOLDING_AND_ACTIVE,
vc, 0, NULL, cb, data);
vc, NULL, cb, data);
}
static void ril_voicecall_release_all_held(struct ofono_voicecall *vc,
ofono_voicecall_cb_t cb, void *data)
{
DBG("");
ril_voicecall_request(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND,
vc, 0, NULL, cb, data);
vc, NULL, cb, data);
}
static void ril_voicecall_release_all_active(struct ofono_voicecall *vc,
ofono_voicecall_cb_t cb, void *data)
{
DBG("");
ril_voicecall_request(RIL_REQUEST_HANGUP_FOREGROUND_RESUME_BACKGROUND,
vc, 0, NULL, cb, data);
vc, NULL, cb, data);
}
static void ril_voicecall_set_udub(struct ofono_voicecall *vc,
ofono_voicecall_cb_t cb, void *data)
{
DBG("");
ril_voicecall_request(RIL_REQUEST_HANGUP_WAITING_OR_BACKGROUND,
vc, 0, NULL, cb, data);
vc, NULL, cb, data);
}
static gboolean ril_voicecall_enable_supp_svc(struct ril_voicecall *vd)
static void ril_voicecall_enable_supp_svc(struct ril_voicecall *vd)
{
GRilIoRequest *req = grilio_request_sized_new(8);
grilio_request_append_int32(req, 1); /* size of array */
grilio_request_append_int32(req, 1); /* notifications enabled */
GRilIoRequest *req = grilio_request_array_int32_new(1, 1);
grilio_request_set_timeout(req, VOICECALL_BLOCK_TIMEOUT_MS);
grilio_request_set_blocking(req, TRUE);
grilio_queue_send_request(vd->q, req,
RIL_REQUEST_SET_SUPP_SVC_NOTIFICATION);
grilio_request_unref(req);
/* Makes this a single shot */
return FALSE;
}
static void ril_voicecall_ringback_tone_event(GRilIoChannel *io,
@@ -755,12 +833,10 @@ static void ril_voicecall_ecclist_changed(struct ril_ecclist *list, void *data)
ofono_voicecall_en_list_notify(vd->vc, vd->ecclist->list);
}
static gboolean ril_delayed_register(gpointer user_data)
static void ril_voicecall_register(gpointer user_data)
{
struct ril_voicecall *vd = user_data;
GASSERT(vd->timer_id);
vd->timer_id = 0;
ofono_voicecall_register(vd->vc);
/* Emergency Call Codes */
@@ -794,15 +870,13 @@ static gboolean ril_delayed_register(gpointer user_data)
grilio_channel_add_unsol_event_handler(vd->io,
ril_voicecall_ringback_tone_event,
RIL_UNSOL_RINGBACK_TONE, vd);
/* This makes the timeout a single-shot */
return FALSE;
}
static int ril_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor,
void *data)
{
struct ril_modem *modem = data;
const struct ril_slot_config *cfg = &modem->config;
struct ril_voicecall *vd;
DBG("");
@@ -810,13 +884,17 @@ static int ril_voicecall_probe(struct ofono_voicecall *vc, unsigned int vendor,
vd->io = grilio_channel_ref(ril_modem_io(modem));
vd->q = grilio_queue_new(vd->io);
vd->dtmf_queue = gutil_ring_new();
vd->local_hangup_reasons = gutil_ints_ref(cfg->local_hangup_reasons);
vd->remote_hangup_reasons = gutil_ints_ref(cfg->remote_hangup_reasons);
vd->local_release_ids = gutil_int_array_new();
vd->idleq = gutil_idle_queue_new();
vd->vc = vc;
vd->timer_id = g_idle_add(ril_delayed_register, vd);
if (modem->ecclist_file) {
vd->ecclist = ril_ecclist_new(modem->ecclist_file);
}
ril_voicecall_clear_dtmf_queue(vd);
ofono_voicecall_set_data(vc, vd);
gutil_idle_queue_add(vd->idleq, ril_voicecall_register, vd);
return 0;
}
@@ -828,10 +906,6 @@ static void ril_voicecall_remove(struct ofono_voicecall *vc)
ofono_voicecall_set_data(vc, NULL);
g_slist_free_full(vd->calls, g_free);
if (vd->timer_id > 0) {
g_source_remove(vd->timer_id);
}
ril_ecclist_remove_handler(vd->ecclist, vd->ecclist_change_id);
ril_ecclist_unref(vd->ecclist);
@@ -841,6 +915,10 @@ static void ril_voicecall_remove(struct ofono_voicecall *vc)
grilio_queue_cancel_all(vd->q, FALSE);
grilio_queue_unref(vd->q);
gutil_ring_unref(vd->dtmf_queue);
gutil_ints_unref(vd->local_hangup_reasons);
gutil_ints_unref(vd->remote_hangup_reasons);
gutil_int_array_free(vd->local_release_ids, TRUE);
gutil_idle_queue_free(vd->idleq);
g_free(vd);
}
@@ -851,7 +929,7 @@ const struct ofono_voicecall_driver ril_voicecall_driver = {
.dial = ril_voicecall_dial,
.answer = ril_voicecall_answer,
.hangup_all = ril_voicecall_hangup_all,
.release_specific = ril_voicecall_hangup_specific,
.release_specific = ril_voicecall_release_specific,
.send_tones = ril_voicecall_send_dtmf,
.create_multiparty = ril_voicecall_create_multiparty,
.transfer = ril_voicecall_transfer,

View File

@@ -0,0 +1,213 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2008-2016 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define _GNU_SOURCE
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <glib.h>
#include <ofono/log.h>
#include <ofono/modem.h>
#include <ofono/cbs.h>
#include "util.h"
#include <gril.h>
#include <parcel.h>
#include "rilmodem.h"
#include "vendor.h"
struct cbs_data {
GRil *ril;
unsigned int vendor;
};
static void ril_cbs_set_cb(struct ril_msg *message, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_cbs_set_cb_t cb = cbd->cb;
struct cbs_data *cd = cbd->user;
if (message->error == RIL_E_SUCCESS) {
CALLBACK_WITH_SUCCESS(cb, cbd->data);
} else {
ofono_error("%s RILD reply failure: %s",
g_ril_request_id_to_string(cd->ril, message->req),
ril_error_to_string(message->error));
CALLBACK_WITH_FAILURE(cb, cbd->data);
}
}
static void ril_cbs_set_topics(struct ofono_cbs *cbs, const char *topics,
ofono_cbs_set_cb_t cb, void *user_data)
{
struct cbs_data *cd = ofono_cbs_get_data(cbs);
struct cb_data *cbd = cb_data_new(cb, user_data, cd);
int i = 0, from, to;
const char *p, *pto;
char **segments;
struct parcel rilp;
segments = g_strsplit(topics, ",", 0);
while (segments[i])
i++;
parcel_init(&rilp);
parcel_w_int32(&rilp, i);
i = 0;
while (segments[i]) {
p = segments[i++];
from = atoi(p);
to = from;
pto = strchr(p, '-');
if (pto)
to = atoi(pto + 1);
parcel_w_int32(&rilp, from);
parcel_w_int32(&rilp, to);
parcel_w_int32(&rilp, 0);
parcel_w_int32(&rilp, 0xFF);
parcel_w_int32(&rilp, 1);
}
g_strfreev(segments);
if (g_ril_send(cd->ril, RIL_REQUEST_GSM_SET_BROADCAST_SMS_CONFIG, &rilp,
ril_cbs_set_cb, cbd, g_free) > 0)
return;
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, user_data);
}
static void ril_cbs_clear_topics(struct ofono_cbs *cbs,
ofono_cbs_set_cb_t cb, void *user_data)
{
ril_cbs_set_topics(cbs, "", cb, user_data);
}
static void ril_cbs_received(struct ril_msg *message, gpointer user_data)
{
struct ofono_cbs *cbs = user_data;
struct cbs_data *cd = ofono_cbs_get_data(cbs);
struct parcel rilp;
int pdulen;
unsigned char *pdu;
g_ril_print_unsol_no_args(cd->ril, message);
DBG("req: %d; data_len: %d", message->req, (int) message->buf_len);
g_ril_init_parcel(message, &rilp);
pdu = parcel_r_raw(&rilp, &pdulen);
if (!pdu || pdulen != 88) {
ofono_error("%s: it isn't a gsm cell broadcast msg", __func__);
return;
}
ofono_cbs_notify(cbs, pdu, pdulen);
g_free(pdu);
}
static void ril_cbs_register(const struct ofono_error *error, void *data)
{
struct ofono_cbs *cbs = data;
struct cbs_data *cd = ofono_cbs_get_data(cbs);
g_ril_register(cd->ril, RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS,
ril_cbs_received, cbs);
ofono_cbs_register(cbs);
}
static void get_cbs_config_cb(struct ril_msg *message,
gpointer user_data)
{
struct ofono_cbs *cbs = user_data;
if (message->error != RIL_E_SUCCESS) {
ofono_error("%s: RIL error %s", __func__,
ril_error_to_string(message->error));
ofono_cbs_remove(cbs);
return;
}
ril_cbs_clear_topics(cbs, ril_cbs_register, cbs);
}
static int ril_cbs_probe(struct ofono_cbs *cbs, unsigned int vendor,
void *user)
{
GRil *ril = user;
struct cbs_data *data;
data = g_new0(struct cbs_data, 1);
data->ril = g_ril_clone(ril);
data->vendor = vendor;
ofono_cbs_set_data(cbs, data);
if (g_ril_send(ril, RIL_REQUEST_GSM_GET_BROADCAST_SMS_CONFIG, NULL,
get_cbs_config_cb, cbs, NULL) == 0)
ofono_error("%s: send failed", __func__);
return 0;
}
static void ril_cbs_remove(struct ofono_cbs *cbs)
{
struct cbs_data *data = ofono_cbs_get_data(cbs);
ofono_cbs_set_data(cbs, NULL);
g_ril_unref(data->ril);
g_free(data);
}
static struct ofono_cbs_driver driver = {
.name = RILMODEM,
.probe = ril_cbs_probe,
.remove = ril_cbs_remove,
.set_topics = ril_cbs_set_topics,
.clear_topics = ril_cbs_clear_topics,
};
void ril_cbs_init(void)
{
ofono_cbs_driver_register(&driver);
}
void ril_cbs_exit(void)
{
ofono_cbs_driver_unregister(&driver);
}

View File

@@ -198,6 +198,7 @@ static void ril_rat_mode_cb(struct ril_msg *message, gpointer user_data)
* capabilities, so it is sort of the default for MTK modems.
*/
switch (net_type) {
case PREF_NET_TYPE_WCDMA:
case PREF_NET_TYPE_GSM_WCDMA:
case PREF_NET_TYPE_GSM_WCDMA_AUTO:
mode = OFONO_RADIO_ACCESS_MODE_UMTS;

View File

@@ -53,6 +53,7 @@ static int rilmodem_init(void)
ril_call_barring_init();
ril_netmon_init();
ril_stk_init();
ril_cbs_init();
return 0;
}
@@ -76,6 +77,7 @@ static void rilmodem_exit(void)
ril_call_barring_exit();
ril_netmon_exit();
ril_stk_exit();
ril_cbs_exit();
}
OFONO_PLUGIN_DEFINE(rilmodem, "RIL modem driver", VERSION,

View File

@@ -75,3 +75,6 @@ extern void ril_netmon_exit(void);
extern void ril_stk_init(void);
extern void ril_stk_exit(void);
extern void ril_cbs_init(void);
extern void ril_cbs_exit(void);

View File

@@ -79,7 +79,7 @@
* The same applies to the app_type.
*/
static void ril_pin_change_state(struct ofono_sim *sim,
static void ril_set_facility_lock(struct ofono_sim *sim,
enum ofono_sim_password_type passwd_type,
int enable, const char *passwd,
ofono_sim_lock_unlock_cb_t cb, void *data);
@@ -1083,7 +1083,7 @@ static void ril_query_passwd_state(struct ofono_sim *sim,
CALLBACK_WITH_SUCCESS(cb, sd->passwd_state, data);
}
static void ril_pin_change_state_cb(struct ril_msg *message, gpointer user_data)
static void ril_enter_sim_pin_cb(struct ril_msg *message, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_sim_lock_unlock_cb_t cb = cbd->cb;
@@ -1101,36 +1101,17 @@ static void ril_pin_change_state_cb(struct ril_msg *message, gpointer user_data)
g_ril_init_parcel(message, &rilp);
/* maguro/infineon: no data is returned */
if (parcel_data_avail(&rilp) == 0)
goto done;
parcel_r_int32(&rilp);
switch (g_ril_vendor(sd->ril)) {
case OFONO_RIL_VENDOR_AOSP:
case OFONO_RIL_VENDOR_QCOM_MSIM:
/*
* The number of retries is valid only when a wrong password has
* been introduced in Nexus 4. TODO: check Nexus 5 behaviour.
*/
if (message->error == RIL_E_PASSWORD_INCORRECT)
sd->retries[sd->passwd_type] = parcel_r_int32(&rilp);
if (message->error == RIL_E_SUCCESS)
sd->retries[sd->passwd_type] = -1;
else
sd->retries[sd->passwd_type] = parcel_r_int32(&rilp);
g_ril_append_print_buf(sd->ril, "{%d}",
sd->retries[sd->passwd_type]);
g_ril_print_response(sd->ril, message);
g_ril_append_print_buf(sd->ril, "{%d}",
sd->retries[sd->passwd_type]);
g_ril_print_response(sd->ril, message);
break;
/* Taken care of elsewhere */
case OFONO_RIL_VENDOR_INFINEON:
case OFONO_RIL_VENDOR_MTK:
break;
default:
break;
}
done:
if (message->error == RIL_E_SUCCESS) {
CALLBACK_WITH_SUCCESS(cb, cbd->data);
return;
@@ -1167,30 +1148,13 @@ static void ril_pin_send(struct ofono_sim *sim, const char *passwd,
g_ril_append_print_buf(sd->ril, "(%s,aid=%s)", passwd, sd->aid_str);
if (g_ril_send(sd->ril, RIL_REQUEST_ENTER_SIM_PIN, &rilp,
ril_pin_change_state_cb, cbd, g_free) > 0)
ril_enter_sim_pin_cb, cbd, g_free) > 0)
return;
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, data);
}
static void enter_pin_done(const struct ofono_error *error, void *data)
{
struct change_state_cbd *csd = data;
struct sim_data *sd = ofono_sim_get_data(csd->sim);
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
ofono_error("%s: wrong password", __func__);
sd->unlock_pending = FALSE;
CALLBACK_WITH_FAILURE(csd->cb, csd->data);
} else {
ril_pin_change_state(csd->sim, csd->passwd_type, csd->enable,
csd->passwd, csd->cb, csd->data);
}
g_free(csd);
}
static const char *const clck_cpwd_fac[] = {
[OFONO_SIM_PASSWORD_SIM_PIN] = "SC",
[OFONO_SIM_PASSWORD_SIM_PIN2] = "P2",
@@ -1204,7 +1168,45 @@ static const char *const clck_cpwd_fac[] = {
#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
static void ril_pin_change_state(struct ofono_sim *sim,
static void ril_set_facility_lock_cb(struct ril_msg *message,
gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_sim_lock_unlock_cb_t cb = cbd->cb;
struct ofono_sim *sim = cbd->user;
struct sim_data *sd = ofono_sim_get_data(sim);
struct parcel rilp;
/*
* There is no reason to ask SIM status until
* unsolicited sim status change indication
* Looks like state does not change before that.
*/
DBG("Enter password: type %d, result %d",
sd->passwd_type, message->error);
g_ril_init_parcel(message, &rilp);
parcel_r_int32(&rilp);
if (message->error == RIL_E_SUCCESS)
sd->retries[sd->passwd_type] = -1;
else
sd->retries[sd->passwd_type] = parcel_r_int32(&rilp);
g_ril_append_print_buf(sd->ril, "{%d}",
sd->retries[sd->passwd_type]);
g_ril_print_response(sd->ril, message);
if (message->error == RIL_E_SUCCESS) {
CALLBACK_WITH_SUCCESS(cb, cbd->data);
return;
}
CALLBACK_WITH_FAILURE(cb, cbd->data);
}
static void ril_set_facility_lock(struct ofono_sim *sim,
enum ofono_sim_password_type passwd_type,
int enable, const char *passwd,
ofono_sim_lock_unlock_cb_t cb, void *data)
@@ -1213,29 +1215,8 @@ static void ril_pin_change_state(struct ofono_sim *sim,
struct cb_data *cbd;
struct parcel rilp;
/*
* If we want to unlock a password that has not been entered yet,
* we enter it before trying to unlock. We need sd->unlock_pending as
* the password still has not yet been refreshed when this function is
* called from enter_pin_done().
*/
if (ofono_sim_get_password_type(sim) == passwd_type
&& enable == FALSE && sd->unlock_pending == FALSE) {
struct change_state_cbd *csd = g_malloc0(sizeof(*csd));
csd->sim = sim;
csd->passwd_type = passwd_type;
csd->enable = enable;
csd->passwd = passwd;
csd->cb = cb;
csd->data = data;
sd->unlock_pending = TRUE;
ril_pin_send(sim, passwd, enter_pin_done, csd);
return;
}
sd->unlock_pending = FALSE;
sd->passwd_type = passwd_type;
if (passwd_type >= ARRAY_SIZE(clck_cpwd_fac) ||
clck_cpwd_fac[passwd_type] == NULL)
@@ -1257,7 +1238,7 @@ static void ril_pin_change_state(struct ofono_sim *sim,
sd->aid_str);
if (g_ril_send(sd->ril, RIL_REQUEST_SET_FACILITY_LOCK, &rilp,
ril_pin_change_state_cb, cbd, g_free) > 0)
ril_set_facility_lock_cb, cbd, g_free) > 0)
return;
g_free(cbd);
@@ -1265,6 +1246,37 @@ error:
CALLBACK_WITH_FAILURE(cb, data);
}
static void ril_enter_sim_puk_cb(struct ril_msg *message, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_sim_lock_unlock_cb_t cb = cbd->cb;
struct ofono_sim *sim = cbd->user;
struct sim_data *sd = ofono_sim_get_data(sim);
struct parcel rilp;
g_ril_init_parcel(message, &rilp);
parcel_r_int32(&rilp);
if (message->error != RIL_E_SUCCESS) {
sd->retries[OFONO_SIM_PASSWORD_SIM_PUK] = parcel_r_int32(&rilp);
} else {
sd->retries[OFONO_SIM_PASSWORD_SIM_PIN] = -1;
sd->retries[OFONO_SIM_PASSWORD_SIM_PUK] = -1;
}
g_ril_append_print_buf(sd->ril, "{%d}",
sd->retries[OFONO_SIM_PASSWORD_SIM_PUK]);
g_ril_print_response(sd->ril, message);
if (message->error == RIL_E_SUCCESS) {
CALLBACK_WITH_SUCCESS(cb, cbd->data);
return;
}
CALLBACK_WITH_FAILURE(cb, cbd->data);
}
static void ril_pin_send_puk(struct ofono_sim *sim,
const char *puk, const char *passwd,
ofono_sim_lock_unlock_cb_t cb, void *data)
@@ -1286,7 +1298,7 @@ static void ril_pin_send_puk(struct ofono_sim *sim,
puk, passwd, sd->aid_str);
if (g_ril_send(sd->ril, RIL_REQUEST_ENTER_SIM_PUK, &rilp,
ril_pin_change_state_cb, cbd, g_free) > 0)
ril_enter_sim_puk_cb, cbd, g_free) > 0)
return;
g_free(cbd);
@@ -1324,7 +1336,7 @@ static void ril_change_passwd(struct ofono_sim *sim,
g_ril_append_print_buf(sd->ril, "(old=%s,new=%s,aid=%s)",
old_passwd, new_passwd, sd->aid_str);
if (g_ril_send(sd->ril, request, &rilp, ril_pin_change_state_cb,
if (g_ril_send(sd->ril, request, &rilp, ril_enter_sim_pin_cb,
cbd, g_free) > 0)
return;
@@ -1413,12 +1425,17 @@ static void ril_query_facility_lock_cb(struct ril_msg *message,
struct sim_data *sd = cbd->user;
struct parcel rilp;
ofono_bool_t status;
int numparams;
if (message->error != RIL_E_SUCCESS)
goto error;
g_ril_init_parcel(message, &rilp);
numparams = parcel_r_int32(&rilp);
if (numparams < 1)
goto error;
status = (ofono_bool_t) parcel_r_int32(&rilp);
g_ril_append_print_buf(sd->ril, "{%d}", status);
@@ -1437,7 +1454,7 @@ static void ril_query_facility_lock(struct ofono_sim *sim,
void *data)
{
struct sim_data *sd = ofono_sim_get_data(sim);
struct cb_data *cbd = cb_data_new(cb, data, sim);
struct cb_data *cbd = cb_data_new(cb, data, sd);
struct parcel rilp;
parcel_init(&rilp);
@@ -1483,7 +1500,7 @@ static struct ofono_sim_driver driver = {
.query_pin_retries = ril_query_pin_retries,
.reset_passwd = ril_pin_send_puk,
.change_passwd = ril_change_passwd,
.lock = ril_pin_change_state,
.lock = ril_set_facility_lock,
.query_facility_lock = ril_query_facility_lock,
};

View File

@@ -261,6 +261,7 @@ static void ublox_send_uauthreq(struct ofono_gprs_context *gc,
case OFONO_GPRS_AUTH_METHOD_PAP:
auth = 1;
break;
case OFONO_GPRS_AUTH_METHOD_ANY:
case OFONO_GPRS_AUTH_METHOD_CHAP:
auth = 2;
break;

View File

@@ -598,6 +598,13 @@ void g_at_mux_unref(GAtMux *mux)
}
}
static void read_watcher_destroy_notify(gpointer user_data)
{
GAtMux *mux = user_data;
mux->read_watch = 0;
}
gboolean g_at_mux_start(GAtMux *mux)
{
if (mux->channel == NULL)
@@ -611,7 +618,8 @@ gboolean g_at_mux_start(GAtMux *mux)
mux->read_watch = g_io_add_watch_full(mux->channel, G_PRIORITY_DEFAULT,
G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
received_data, mux, NULL);
received_data, mux,
read_watcher_destroy_notify);
mux->shutdown = FALSE;

View File

@@ -374,6 +374,12 @@ static void handle_response(struct ril_s *p, struct ril_msg *message)
if (req->callback)
req->callback(message, req->user_data);
/* gril may have been destroyed in the request callback */
if (p->destroyed) {
ril_request_destroy(req);
return;
}
len = g_queue_get_length(p->out_queue);
for (i = 0; i < len; i++) {

View File

@@ -75,7 +75,8 @@ extern char print_buf[];
#define g_ril_print_request(gril, token, req) \
G_RIL_TRACE(gril, "[%d,%04d]> %s %s", \
g_ril_get_slot(gril), token, \
g_ril_request_id_to_string(gril, req), print_buf)
g_ril_request_id_to_string(gril, req), print_buf); \
print_buf[0] = '\0';
#define g_ril_print_request_no_args(gril, token, req) \
G_RIL_TRACE(gril, "[%d,%04d]> %s", \
g_ril_get_slot(gril), token, \
@@ -85,7 +86,8 @@ extern char print_buf[];
g_ril_get_slot(gril), \
message->serial_no, \
g_ril_request_id_to_string(gril, message->req), \
print_buf)
print_buf); \
print_buf[0] = '\0';
#define g_ril_print_response_no_args(gril, message) \
G_RIL_TRACE(gril, "[%d,%04d]< %s", \
g_ril_get_slot(gril), message->serial_no, \

View File

@@ -49,13 +49,14 @@ enum ofono_gprs_context_type {
};
enum ofono_gprs_auth_method {
OFONO_GPRS_AUTH_METHOD_CHAP = 0,
OFONO_GPRS_AUTH_METHOD_ANY = 0,
OFONO_GPRS_AUTH_METHOD_NONE,
OFONO_GPRS_AUTH_METHOD_CHAP,
OFONO_GPRS_AUTH_METHOD_PAP,
};
struct ofono_gprs_primary_context {
unsigned int cid;
int direction;
char apn[OFONO_GPRS_MAX_APN_LENGTH + 1];
char username[OFONO_GPRS_MAX_USERNAME_LENGTH + 1];
char password[OFONO_GPRS_MAX_PASSWORD_LENGTH + 1];

View File

@@ -114,6 +114,7 @@ int ofono_netreg_get_status(struct ofono_netreg *netreg);
int ofono_netreg_get_technology(struct ofono_netreg *netreg);
const char *ofono_netreg_get_mcc(struct ofono_netreg *netreg);
const char *ofono_netreg_get_mnc(struct ofono_netreg *netreg);
const char *ofono_netreg_get_name(struct ofono_netreg *netreg);
struct sim_spdi *ofono_netreg_get_spdi(struct ofono_netreg *netreg);
#ifdef __cplusplus

View File

@@ -0,0 +1,121 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 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
* 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.
*/
#ifndef SAILFISHOS_MANAGER_H
#define SAILFISHOS_MANAGER_H
struct ofono_modem;
#include <ofono/types.h>
#include <ofono/radio-settings.h>
#include <glib.h>
struct sailfish_manager;
struct sailfish_slot;
struct sailfish_slot_impl;
struct sailfish_slot_driver;
struct sailfish_slot_driver_reg;
struct sailfish_slot_manager;
struct sailfish_slot_manager_impl;
typedef void (*sailfish_slot_manager_impl_cb_t)
(struct sailfish_slot_manager_impl *impl, void *user_data);
typedef struct sailfish_slot {
const char *path;
const char *imei;
const char *imeisv;
gboolean sim_present;
gboolean enabled;
} const *sailfish_slot_ptr;
struct sailfish_manager {
const char *mms_imsi;
const char *mms_path;
const char *default_voice_imsi;
const char *default_data_imsi;
const char *default_voice_path;
const char *default_data_path;
const sailfish_slot_ptr *slots;
gboolean ready;
};
enum sailfish_sim_state {
SAILFISH_SIM_STATE_UNKNOWN,
SAILFISH_SIM_STATE_ABSENT,
SAILFISH_SIM_STATE_PRESENT,
SAILFISH_SIM_STATE_ERROR
};
enum sailfish_data_role {
SAILFISH_DATA_ROLE_NONE, /* Data not allowed */
SAILFISH_DATA_ROLE_MMS, /* Data is allowed at any speed */
SAILFISH_DATA_ROLE_INTERNET /* Data is allowed at full speed */
};
/* Register/unregister the driver */
struct sailfish_slot_driver_reg *sailfish_slot_driver_register
(const struct sailfish_slot_driver *d);
void sailfish_slot_driver_unregister(struct sailfish_slot_driver_reg *r);
/* For use by the driver implementations */
void sailfish_manager_foreach_slot_manager
(struct sailfish_slot_driver_reg *r,
sailfish_slot_manager_impl_cb_t cb, void *user_data);
struct sailfish_slot *sailfish_manager_slot_add
(struct sailfish_slot_manager *m, struct sailfish_slot_impl *i,
const char *path, enum ofono_radio_access_mode techs,
const char *imei, const char *imeisv,
enum sailfish_sim_state sim_state);
void sailfish_manager_imei_obtained(struct sailfish_slot *s, const char *imei);
void sailfish_manager_imeisv_obtained(struct sailfish_slot *s,
const char *imeisv);
void sailfish_manager_set_sim_state(struct sailfish_slot *s,
enum sailfish_sim_state state);
void sailfish_slot_manager_started(struct sailfish_slot_manager *m);
void sailfish_manager_slot_error(struct sailfish_slot *s, const char *key,
const char *message);
void sailfish_manager_error(struct sailfish_slot_manager *m, const char *key,
const char *message);
/* Callbacks provided by slot plugins */
struct sailfish_slot_driver {
const char *name;
int priority;
/* Slot manager methods */
struct sailfish_slot_manager_impl *(*manager_create)
(struct sailfish_slot_manager *m);
guint (*manager_start)(struct sailfish_slot_manager_impl *s);
void (*manager_cancel_start)(struct sailfish_slot_manager_impl *s,
guint id);
void (*manager_free)(struct sailfish_slot_manager_impl *s);
/* Slot methods */
void (*slot_enabled_changed)(struct sailfish_slot_impl *s);
void (*slot_set_data_role)(struct sailfish_slot_impl *s,
enum sailfish_data_role role);
void (*slot_free)(struct sailfish_slot_impl *s);
};
#endif /* SAILFISHOS_MANAGER_H */
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -0,0 +1,81 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 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
* 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.
*/
#ifndef SAILFISH_WATCH_H
#define SAILFISH_WATCH_H
struct ofono_modem;
struct ofono_sim;
struct ofono_netreg;
#include <glib.h>
#include <glib-object.h>
/* This object watches ofono modem and various related things */
struct sailfish_watch_priv;
struct sailfish_watch {
GObject object;
struct sailfish_watch_priv *priv;
const char *path;
/* Modem */
struct ofono_modem *modem;
gboolean online;
/* OFONO_ATOM_TYPE_SIM */
struct ofono_sim *sim;
const char *iccid;
const char *imsi;
const char *spn;
/* OFONO_ATOM_TYPE_NETREG */
struct ofono_netreg *netreg;
};
typedef void (*sailfish_watch_cb_t)(struct sailfish_watch *w, void *user_data);
struct sailfish_watch *sailfish_watch_new(const char *path);
struct sailfish_watch *sailfish_watch_ref(struct sailfish_watch *w);
void sailfish_watch_unref(struct sailfish_watch *w);
gulong sailfish_watch_add_modem_changed_handler(struct sailfish_watch *w,
sailfish_watch_cb_t cb, void *user_data);
gulong sailfish_watch_add_online_changed_handler(struct sailfish_watch *w,
sailfish_watch_cb_t cb, void *user_data);
gulong sailfish_watch_add_sim_changed_handler(struct sailfish_watch *w,
sailfish_watch_cb_t cb, void *user_data);
gulong sailfish_watch_add_sim_state_changed_handler(struct sailfish_watch *w,
sailfish_watch_cb_t cb, void *user_data);
gulong sailfish_watch_add_iccid_changed_handler(struct sailfish_watch *w,
sailfish_watch_cb_t cb, void *user_data);
gulong sailfish_watch_add_imsi_changed_handler(struct sailfish_watch *w,
sailfish_watch_cb_t cb, void *user_data);
gulong sailfish_watch_add_spn_changed_handler(struct sailfish_watch *w,
sailfish_watch_cb_t cb, void *user_data);
gulong sailfish_watch_add_netreg_changed_handler(struct sailfish_watch *w,
sailfish_watch_cb_t cb, void *user_data);
void sailfish_watch_remove_handler(struct sailfish_watch *w, gulong id);
void sailfish_watch_remove_handlers(struct sailfish_watch *w, gulong *ids,
int count);
#define sailfish_watch_remove_all_handlers(w,ids) \
sailfish_watch_remove_handlers(w, ids, G_N_ELEMENTS(ids))
#endif /* SAILFISH_WATCH_H */
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -172,6 +172,8 @@ void ofono_voicecall_ssn_mt_notify(struct ofono_voicecall *vc, unsigned int id,
int code, int index,
const struct ofono_phone_number *ph);
struct ofono_call *ofono_voicecall_find_call(struct ofono_voicecall *vc,
unsigned int id);
void ofono_voicecall_ringback_tone_notify(struct ofono_voicecall *vc,
const ofono_bool_t playTone);

View File

@@ -53,6 +53,9 @@ const char *mbpi_database = MBPI_DATABASE;
enum ofono_gprs_proto mbpi_default_internet_proto = OFONO_GPRS_PROTO_IPV4V6;
enum ofono_gprs_proto mbpi_default_mms_proto = OFONO_GPRS_PROTO_IP;
enum ofono_gprs_proto mbpi_default_proto = OFONO_GPRS_PROTO_IP;
enum ofono_gprs_auth_method mbpi_default_auth_method = OFONO_GPRS_AUTH_METHOD_ANY;
#define OFONO_GPRS_AUTH_METHOD_UNSPECIFIED ((enum ofono_gprs_auth_method)(-1))
#define _(x) case x: return (#x)
@@ -166,6 +169,10 @@ static void authentication_start(GMarkupParseContext *context,
*auth_method = OFONO_GPRS_AUTH_METHOD_CHAP;
else if (strcmp(text, "pap") == 0)
*auth_method = OFONO_GPRS_AUTH_METHOD_PAP;
else if (strcmp(text, "any") == 0)
*auth_method = OFONO_GPRS_AUTH_METHOD_ANY;
else if (strcmp(text, "none") == 0)
*auth_method = OFONO_GPRS_AUTH_METHOD_NONE;
else
mbpi_g_set_error(context, error, G_MARKUP_ERROR,
G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE,
@@ -344,7 +351,7 @@ static void apn_handler(GMarkupParseContext *context, struct gsm_data *gsm,
ap->apn = g_strdup(apn);
ap->type = OFONO_GPRS_CONTEXT_TYPE_INTERNET;
ap->proto = mbpi_default_proto;
ap->auth_method = OFONO_GPRS_AUTH_METHOD_CHAP;
ap->auth_method = OFONO_GPRS_AUTH_METHOD_UNSPECIFIED;
g_markup_parse_context_push(context, &apn_parser, ap);
}
@@ -414,6 +421,17 @@ static void gsm_end(GMarkupParseContext *context, const gchar *element_name,
if (ap == NULL)
return;
/* Fix the authentication method if none was specified */
if (ap->auth_method == OFONO_GPRS_AUTH_METHOD_UNSPECIFIED) {
if ((!ap->username || !ap->username[0]) &&
(!ap->password || !ap->password[0])) {
/* No username or password => no authentication */
ap->auth_method = OFONO_GPRS_AUTH_METHOD_NONE;
} else {
ap->auth_method = mbpi_default_auth_method;
}
}
if (gsm->allow_duplicates == FALSE) {
GSList *l;

View File

@@ -23,6 +23,7 @@ extern const char *mbpi_database;
extern enum ofono_gprs_proto mbpi_default_internet_proto;
extern enum ofono_gprs_proto mbpi_default_mms_proto;
extern enum ofono_gprs_proto mbpi_default_proto;
extern enum ofono_gprs_auth_method mbpi_default_auth_method;
const char *mbpi_ap_type(enum ofono_gprs_context_type type);

View File

@@ -3,7 +3,6 @@
* oFono - Open Source Telephony
*
* Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
* Copyright (C) 2013-2016 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
@@ -24,7 +23,6 @@
#include <config.h>
#endif
#define _GNU_SOURCE
#include <errno.h>
#include <string.h>
@@ -37,114 +35,9 @@
#include <ofono/modem.h>
#include <ofono/gprs-provision.h>
#include "provision.h"
#include "mbpi.h"
struct provision_ap_defaults {
enum ofono_gprs_context_type type;
const char *name;
const char *apn;
};
static gboolean provision_match_name(const struct ofono_gprs_provision_data *ap,
const char* spn)
{
return (ap->provider_name && strcasestr(ap->provider_name, spn)) ||
(ap->name && strcasestr(ap->name, spn)) ||
(ap->apn && strcasestr(ap->apn, spn));
}
static void provision_free_ap(gpointer data)
{
mbpi_ap_free(data);
}
static gint provision_compare_ap(gconstpointer a, gconstpointer b, gpointer data)
{
const struct ofono_gprs_provision_data *ap1 = a;
const struct ofono_gprs_provision_data *ap2 = b;
const char* spn = data;
if (spn) {
const gboolean match1 = provision_match_name(ap1, spn);
const gboolean match2 = provision_match_name(ap2, spn);
if (match1 && !match2) {
return -1;
} else if (match2 && !match1) {
return 1;
}
}
if (ap1->provider_primary && !ap2->provider_primary) {
return -1;
} else if (ap2->provider_primary && !ap1->provider_primary) {
return 1;
} else {
return 0;
}
}
/* Picks best ap, deletes the rest. Creates one if necessary */
static GSList *provision_pick_best_ap(GSList *list, const char* spn,
const enum ofono_gprs_proto default_proto,
const struct provision_ap_defaults *defaults)
{
/* Sort the list */
list = g_slist_sort_with_data(list, provision_compare_ap, (void*)spn);
if (list) {
/* Pick the best one, delete the rest */
GSList *best = list;
g_slist_free_full(g_slist_remove_link(list, best),
provision_free_ap);
return best;
} else {
/* or create one from the default data */
struct ofono_gprs_provision_data *ap =
g_new0(struct ofono_gprs_provision_data, 1);
ap->proto = default_proto;
ap->type = defaults->type;
ap->name = g_strdup(defaults->name);
ap->apn = g_strdup(defaults->apn);
return g_slist_append(NULL, ap);
}
}
/* Returns the list containing exactly one INTERNET and one MMS access point */
static GSList *provision_normalize_apn_list(GSList *apns, const char* spn)
{
static const struct provision_ap_defaults internet_defaults =
{ OFONO_GPRS_CONTEXT_TYPE_INTERNET, "Internet", "internet" };
static const struct provision_ap_defaults mms_defaults =
{ OFONO_GPRS_CONTEXT_TYPE_MMS, "MMS", "mms" };
GSList *internet_apns = NULL;
GSList *mms_apns = NULL;
/* Split internet and mms apns, delete all others */
while (apns) {
GSList *link = apns;
struct ofono_gprs_provision_data *ap = link->data;
apns = g_slist_remove_link(apns, link);
if (ap->type == OFONO_GPRS_CONTEXT_TYPE_INTERNET) {
internet_apns = g_slist_concat(internet_apns, link);
} else if (ap->type == OFONO_GPRS_CONTEXT_TYPE_MMS) {
mms_apns = g_slist_concat(mms_apns, link);
} else {
g_slist_free_full(link, provision_free_ap);
}
}
/* Pick the best ap of each type and concatenate them */
return g_slist_concat(
provision_pick_best_ap(internet_apns, spn,
mbpi_default_internet_proto, &internet_defaults),
provision_pick_best_ap(mms_apns, spn,
mbpi_default_mms_proto, &mms_defaults));
}
int provision_get_settings(const char *mcc, const char *mnc,
static int provision_get_settings(const char *mcc, const char *mnc,
const char *spn,
struct ofono_gprs_provision_data **settings,
int *count)
@@ -155,26 +48,21 @@ int provision_get_settings(const char *mcc, const char *mnc,
int ap_count;
int i;
ofono_info("Provisioning for MCC %s, MNC %s, SPN '%s'", mcc, mnc, spn);
DBG("Provisioning for MCC %s, MNC %s, SPN '%s'", mcc, mnc, spn);
/*
* Passing FALSE to mbpi_lookup_apn() would return
* an empty list if duplicates are found.
*/
apns = mbpi_lookup_apn(mcc, mnc, TRUE, &error);
if (error != NULL) {
ofono_error("%s", error->message);
g_error_free(error);
}
apns = mbpi_lookup_apn(mcc, mnc, FALSE, &error);
if (apns == NULL) {
if (error != NULL) {
ofono_error("%s", error->message);
g_error_free(error);
}
ofono_info("Found %d APs in MBPI", g_slist_length(apns));
apns = provision_normalize_apn_list(apns, spn);
if (apns == NULL)
return -ENOENT;
}
ap_count = g_slist_length(apns);
ofono_info("Provisioning %d APs", ap_count);
DBG("Found %d APs", ap_count);
*settings = g_try_new0(struct ofono_gprs_provision_data, ap_count);
if (*settings == NULL) {
@@ -193,11 +81,11 @@ int provision_get_settings(const char *mcc, const char *mnc,
for (l = apns, i = 0; l; l = l->next, i++) {
struct ofono_gprs_provision_data *ap = l->data;
ofono_info("Name: '%s'", ap->name);
ofono_info("APN: '%s'", ap->apn);
ofono_info("Type: %s", mbpi_ap_type(ap->type));
ofono_info("Username: '%s'", ap->username);
ofono_info("Password: '%s'", ap->password);
DBG("Name: '%s'", ap->name);
DBG("APN: '%s'", ap->apn);
DBG("Type: %s", mbpi_ap_type(ap->type));
DBG("Username: '%s'", ap->username);
DBG("Password: '%s'", ap->password);
memcpy(*settings + i, ap,
sizeof(struct ofono_gprs_provision_data));

View File

@@ -239,6 +239,7 @@ void ril_post_online(struct ofono_modem *modem)
struct ofono_gprs *gprs;
struct ofono_gprs_context *gc;
ofono_cbs_create(modem, rd->vendor, RILMODEM, rd->ril);
ofono_netreg_create(modem, rd->vendor, RILMODEM, rd->ril);
ofono_ussd_create(modem, rd->vendor, RILMODEM, rd->ril);
ofono_call_settings_create(modem, rd->vendor, RILMODEM, rd->ril);
@@ -409,15 +410,30 @@ int ril_enable(struct ofono_modem *modem)
return -EINPROGRESS;
}
static void power_off_cb(struct ril_msg *message, gpointer user_data)
{
struct cb_data *cbd = user_data;
struct ril_data *rd = cbd->user;
struct ofono_modem *modem = cbd->data;
if (rd) {
g_ril_unref(rd->ril);
rd->ril = NULL;
}
ofono_modem_set_powered(modem, FALSE);
}
int ril_disable(struct ofono_modem *modem)
{
struct ril_data *rd = ofono_modem_get_data(modem);
struct cb_data *cbd = cb_data_new(NULL, modem, rd);
DBG("%p", modem);
ril_send_power(rd, FALSE, NULL, NULL);
ril_send_power(rd, FALSE, power_off_cb, cbd);
return 0;
return -EINPROGRESS;
}
static struct ofono_modem_driver ril_driver = {

View File

@@ -29,6 +29,7 @@
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <unistd.h>
#include <glib.h>
@@ -52,11 +53,22 @@
#include "drivers/rilmodem/rilmodem.h"
#include "drivers/rilmodem/vendor.h"
#include "gdbus.h"
#include "ofono.h"
#define THERMAL_MANAGEMENT_INTERFACE OFONO_SERVICE ".sofia3gr.ThermalManagement"
struct ril_data {
GRil *ril;
};
struct ril_thermal_management {
DBusMessage *pending;
struct ofono_modem *modem;
dbus_bool_t throttling;
};
static int ril_send_power(GRil *ril, ofono_bool_t online,
GRilResponseFunc func,
gpointer user_data,
@@ -129,7 +141,14 @@ static int ril_probe(struct ofono_modem *modem)
static void ril_remove(struct ofono_modem *modem)
{
DBusConnection *conn = ofono_dbus_get_connection();
struct ril_data *rd = ofono_modem_get_data(modem);
const char *path = ofono_modem_get_path(modem);
if (g_dbus_unregister_interface(conn, path,
THERMAL_MANAGEMENT_INTERFACE))
ofono_modem_remove_interface(modem,
THERMAL_MANAGEMENT_INTERFACE);
ofono_modem_set_data(modem, NULL);
@@ -137,6 +156,270 @@ static void ril_remove(struct ofono_modem *modem)
g_free(rd);
}
static void set_rf_power_status_cb(struct ril_msg *message, gpointer user_data)
{
DBusConnection *conn = ofono_dbus_get_connection();
struct ril_thermal_management *tm = user_data;
struct ril_data *rd = ofono_modem_get_data(tm->modem);
const char *path = ofono_modem_get_path(tm->modem);
DBG("");
if (message->error != RIL_E_SUCCESS) {
ofono_error("%s RILD reply failure: %s",
g_ril_request_id_to_string(rd->ril, message->req),
ril_error_to_string(message->error));
__ofono_dbus_pending_reply(&tm->pending,
__ofono_error_failed(tm->pending));
return;
}
/* Change the throttling state */
tm->throttling = tm->throttling ? false : true;
__ofono_dbus_pending_reply(&tm->pending,
dbus_message_new_method_return(tm->pending));
ofono_dbus_signal_property_changed(conn, path,
THERMAL_MANAGEMENT_INTERFACE,
"TransmitPowerThrottling",
DBUS_TYPE_BOOLEAN,
&tm->throttling);
}
static DBusMessage *set_rf_power_status(DBusMessage *msg,
dbus_bool_t enable,
void *data)
{
struct ril_thermal_management *tm = data;
struct ril_data *rd = ofono_modem_get_data(tm->modem);
struct parcel rilp;
int cmd_id;
char buf[4];
DBG("");
if (tm->pending)
return __ofono_error_busy(msg);
parcel_init(&rilp);
parcel_w_int32(&rilp, 2);
/* RIL_OEM_HOOK_STRING_SET_RF_POWER_STATUS = 0x000000AC */
cmd_id = 0x000000AC;
sprintf(buf, "%d", cmd_id);
parcel_w_string(&rilp, buf);
memset(buf, 0, sizeof(buf));
sprintf(buf, "%d", enable ? 1 : 0);
parcel_w_string(&rilp, buf);
g_ril_append_print_buf(rd->ril, "{cmd_id=0x%02X,arg=%s}", cmd_id, buf);
if (g_ril_send(rd->ril, RIL_REQUEST_OEM_HOOK_STRINGS, &rilp,
set_rf_power_status_cb, tm, NULL) == 0)
return __ofono_error_failed(msg);
tm->pending = dbus_message_ref(msg);
return NULL;
}
static DBusMessage *thermal_management_set_property(DBusConnection *conn,
DBusMessage *msg,
void *data)
{
struct ril_thermal_management *tm = data;
DBusMessageIter iter;
DBusMessageIter var;
const char *name;
dbus_bool_t throttling;
DBG("");
if (!ofono_modem_get_online(tm->modem))
return __ofono_error_not_available(msg);
if (!dbus_message_iter_init(msg, &iter))
return __ofono_error_invalid_args(msg);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
return __ofono_error_invalid_args(msg);
dbus_message_iter_get_basic(&iter, &name);
if (!strcmp(name, "TransmitPowerThrottling")) {
dbus_message_iter_next(&iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
return __ofono_error_invalid_args(msg);
dbus_message_iter_recurse(&iter, &var);
if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
return __ofono_error_invalid_args(msg);
dbus_message_iter_get_basic(&var, &throttling);
if (tm->throttling == throttling)
/* Ignore set request if new state == current state */
return dbus_message_new_method_return(msg);
return set_rf_power_status(msg, throttling, tm);
}
return __ofono_error_invalid_args(msg);
}
static DBusMessage *thermal_management_get_properties(DBusConnection *conn,
DBusMessage *msg,
void *data)
{
struct ril_thermal_management *tm = data;
DBusMessage *reply;
DBusMessageIter iter;
DBusMessageIter dict;
DBG("");
reply = dbus_message_new_method_return(msg);
if (reply == NULL)
return NULL;
dbus_message_iter_init_append(reply, &iter);
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
OFONO_PROPERTIES_ARRAY_SIGNATURE,
&dict);
ofono_dbus_dict_append(&dict, "TransmitPowerThrottling",
DBUS_TYPE_BOOLEAN,
&tm->throttling);
dbus_message_iter_close_container(&iter, &dict);
return reply;
}
static const GDBusMethodTable thermal_management_methods[] = {
{ GDBUS_METHOD("GetProperties",
NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
thermal_management_get_properties) },
{ GDBUS_ASYNC_METHOD("SetProperty",
GDBUS_ARGS({ "property", "s" }, { "value", "v" }),
NULL, thermal_management_set_property) },
{}
};
static const GDBusSignalTable thermal_management_signals[] = {
{ GDBUS_SIGNAL("PropertyChanged",
GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
{ }
};
static void thermal_management_cleanup(void *data)
{
struct ril_thermal_management *tm = data;
if (tm->pending)
__ofono_dbus_pending_reply(&tm->pending,
__ofono_error_canceled(tm->pending));
g_free(tm);
}
static void get_rf_power_status_cb(struct ril_msg *message, gpointer user_data)
{
struct ofono_modem *modem = user_data;
struct ril_data *rd = ofono_modem_get_data(modem);
struct ril_thermal_management *tm;
DBusConnection *conn = ofono_dbus_get_connection();
struct parcel rilp;
gint numstr;
gchar *power_status;
char *endptr;
int enabled;
const char *path = ofono_modem_get_path(modem);
DBG("");
if (message->error != RIL_E_SUCCESS) {
ofono_error("%s RILD reply failure: %s",
g_ril_request_id_to_string(rd->ril, message->req),
ril_error_to_string(message->error));
return;
}
g_ril_init_parcel(message, &rilp);
numstr = parcel_r_int32(&rilp);
if (numstr < 1) {
ofono_error("RILD reply empty !");
return;
}
power_status = parcel_r_string(&rilp);
if (power_status == NULL || power_status == '\0')
return;
enabled = strtol(power_status, &endptr, 10);
/*
* power_status == endptr => conversion error
* *endptr != '\0' => partial conversion
*/
if (power_status == endptr || *endptr != '\0')
return;
tm = g_try_new0(struct ril_thermal_management, 1);
if (tm == NULL)
return;
tm->modem = modem;
tm->throttling = (enabled > 0) ? true : false;
if (!g_dbus_register_interface(conn, path, THERMAL_MANAGEMENT_INTERFACE,
thermal_management_methods,
thermal_management_signals,
NULL, tm, thermal_management_cleanup)) {
ofono_error("Could not register %s interface under %s",
THERMAL_MANAGEMENT_INTERFACE, path);
g_free(tm);
return;
}
ofono_modem_add_interface(modem, THERMAL_MANAGEMENT_INTERFACE);
}
static int ril_thermal_management_enable(struct ofono_modem *modem)
{
struct ril_data *rd = ofono_modem_get_data(modem);
struct parcel rilp;
int cmd_id;
char buf[4];
DBG("");
parcel_init(&rilp);
parcel_w_int32(&rilp, 1);
/* RIL_OEM_HOOK_STRING_GET_RF_POWER_STATUS = 0x000000AB */
cmd_id = 0x000000AB;
sprintf(buf, "%d", cmd_id);
parcel_w_string(&rilp, buf);
g_ril_append_print_buf(rd->ril, "{cmd_id=0x%02X}", cmd_id);
if (g_ril_send(rd->ril, RIL_REQUEST_OEM_HOOK_STRINGS, &rilp,
get_rf_power_status_cb, modem, NULL) > 0)
return 0;
/* Error path */
return -EIO;
}
static void ril_pre_sim(struct ofono_modem *modem)
{
struct ril_data *rd = ofono_modem_get_data(modem);
@@ -145,6 +428,7 @@ static void ril_pre_sim(struct ofono_modem *modem)
ofono_devinfo_create(modem, 0, "rilmodem", rd->ril);
ofono_sim_create(modem, 0, "rilmodem", rd->ril);
ril_thermal_management_enable(modem);
}
static void ril_post_sim(struct ofono_modem *modem)
@@ -255,14 +539,45 @@ static int ril_enable(struct ofono_modem *modem)
return -EINPROGRESS;
}
static int ril_disable(struct ofono_modem *modem)
static void ril_send_power_off_cb(struct ril_msg *message, gpointer user_data)
{
struct ofono_modem *modem = (struct ofono_modem *) user_data;
struct ril_data *rd = ofono_modem_get_data(modem);
DBG("%p", modem);
ril_send_power(rd->ril, FALSE, NULL, NULL, NULL);
g_ril_unref(rd->ril);
return 0;
ofono_modem_set_powered(modem, FALSE);
}
static int ril_disable(struct ofono_modem *modem)
{
DBusConnection *conn = ofono_dbus_get_connection();
struct ril_data *rd = ofono_modem_get_data(modem);
const char *path = ofono_modem_get_path(modem);
struct parcel rilp;
int cmd_id;
char buf[4];
DBG("%p", modem);
if (g_dbus_unregister_interface(conn, path,
THERMAL_MANAGEMENT_INTERFACE))
ofono_modem_remove_interface(modem,
THERMAL_MANAGEMENT_INTERFACE);
/* RIL_OEM_HOOK_STRING_SET_MODEM_OFF = 0x000000CF */
cmd_id = 0x000000CF;
sprintf(buf, "%d", cmd_id);
parcel_init(&rilp);
parcel_w_int32(&rilp, 1);
parcel_w_string(&rilp, buf);
g_ril_append_print_buf(rd->ril, "{cmd_id=0x%02X}", cmd_id);
g_ril_send(rd->ril, RIL_REQUEST_OEM_HOOK_STRINGS, &rilp,
ril_send_power_off_cb, modem, NULL);
return -EINPROGRESS;
}
static struct ofono_modem_driver ril_driver = {

362
ofono/plugins/sailfish_bt.c Normal file
View File

@@ -0,0 +1,362 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 Jolla Ltd. 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.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <stdint.h>
#include <glib.h>
#include <ofono.h>
#include <gdbus.h>
#define OFONO_API_SUBJECT_TO_CHANGE
#include <ofono/plugin.h>
#include <ofono/log.h>
#define SFOS_BT_DBUS_CV_INTERFACE "org.nemomobile.ofono.bluetooth.CallVolume"
#define HFP_CALL_VOLUME_MAX 15
struct sfos_bt {
unsigned int emu_watch;
struct ofono_modem *modem;
struct ofono_emulator *em;
unsigned char speaker_volume;
unsigned char microphone_volume;
};
static GSList *modems;
static guint modemwatch_id;
static void set_hfp_microphone_volume(struct sfos_bt *sfos_bt,
unsigned char gain)
{
char buf[64];
snprintf(buf, sizeof(buf), "+VGM:%d", (int) gain);
ofono_emulator_send_unsolicited(sfos_bt->em, buf);
}
static void set_hfp_speaker_volume(struct sfos_bt *sfos_bt,
unsigned char gain)
{
char buf[64];
snprintf(buf, sizeof(buf), "+VGS:%d", (int) gain);
ofono_emulator_send_unsolicited(sfos_bt->em, buf);
}
static DBusMessage *cv_set_property(DBusConnection *conn, DBusMessage *msg,
void *data)
{
struct sfos_bt *sfos_bt = data;
DBusMessageIter iter;
DBusMessageIter var;
const char *property;
if (!dbus_message_iter_init(msg, &iter))
return __ofono_error_invalid_args(msg);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
return __ofono_error_invalid_args(msg);
dbus_message_iter_get_basic(&iter, &property);
dbus_message_iter_next(&iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
return __ofono_error_invalid_args(msg);
dbus_message_iter_recurse(&iter, &var);
if (g_str_equal(property, "SpeakerVolume") == TRUE) {
unsigned char gain;
if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BYTE)
return __ofono_error_invalid_args(msg);
dbus_message_iter_get_basic(&var, &gain);
if (gain > HFP_CALL_VOLUME_MAX)
return __ofono_error_invalid_format(msg);
if (gain == sfos_bt->speaker_volume)
return dbus_message_new_method_return(msg);
DBG("SpeakerVolume:%d", gain);
sfos_bt->speaker_volume = gain;
set_hfp_speaker_volume(sfos_bt, gain);
return dbus_message_new_method_return(msg);
} else if (g_str_equal(property, "MicrophoneVolume") == TRUE) {
unsigned char gain;
if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BYTE)
return __ofono_error_invalid_args(msg);
dbus_message_iter_get_basic(&var, &gain);
if (gain > HFP_CALL_VOLUME_MAX)
return __ofono_error_invalid_format(msg);
if (gain == sfos_bt->microphone_volume)
return dbus_message_new_method_return(msg);
DBG("MicrophoneVolume:%d", gain);
sfos_bt->microphone_volume = gain;
set_hfp_microphone_volume(sfos_bt, gain);
return dbus_message_new_method_return(msg);
} else if (g_str_equal(property, "Muted") == TRUE) {
unsigned char gain;
dbus_bool_t muted;
/*Remove when supported*/
return __ofono_error_not_implemented(msg);
if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
return __ofono_error_invalid_args(msg);
dbus_message_iter_get_basic(&var, &muted);
if (muted)
gain = 0;
else
gain = 7;/* rather gain = sfos->old_mic_vol */
if (gain == sfos_bt->microphone_volume)
return dbus_message_new_method_return(msg);
sfos_bt->microphone_volume = gain;
set_hfp_microphone_volume(sfos_bt, gain);
return dbus_message_new_method_return(msg);
}
return __ofono_error_invalid_args(msg);
}
static const GDBusMethodTable cv_methods[] = {
{ GDBUS_METHOD("SetProperty",
GDBUS_ARGS({ "property", "s" }, { "value", "v" }),
NULL, cv_set_property) },
{ }
};
static const GDBusSignalTable cv_signals[] = {
{ GDBUS_SIGNAL("PropertyChanged",
GDBUS_ARGS({ "property", "s" }, { "value", "v" })) },
{ }
};
int sfos_bt_call_volume_set(struct ofono_modem *modem, unsigned char volume,
const char *gain)
{
DBusConnection *conn = ofono_dbus_get_connection();
const char *path = ofono_modem_get_path(modem);
return ofono_dbus_signal_property_changed(conn, path,
SFOS_BT_DBUS_CV_INTERFACE,
gain,
DBUS_TYPE_BYTE, &volume);
}
static void set_gain(struct ofono_emulator *em,
struct ofono_emulator_request *req,
void *userdata, const char *gain)
{
struct sfos_bt *sfos_bt = userdata;
struct ofono_modem *modem = sfos_bt->modem;
struct ofono_error result;
unsigned char volume;
int val;
result.error = 0;
switch (ofono_emulator_request_get_type(req)) {
case OFONO_EMULATOR_REQUEST_TYPE_SET:
if (ofono_emulator_request_next_number(req, &val) == FALSE)
goto fail;
if (val < 0 || val > 0xffff || val > HFP_CALL_VOLUME_MAX)
goto fail;
DBG("gain:%d", val);
volume = (unsigned char) val;
if (sfos_bt_call_volume_set(modem, volume, gain)<= 0)
goto fail;
if (!g_strcmp0(gain, "SpeakerVolume"))
sfos_bt->speaker_volume = volume;
else
sfos_bt->microphone_volume = volume;
result.type = OFONO_ERROR_TYPE_NO_ERROR;
ofono_emulator_send_final(em, &result);
break;
default:
fail:
result.type = OFONO_ERROR_TYPE_FAILURE;
ofono_emulator_send_final(em, &result);
break;
}
}
static void sfos_bt_vgm_cb(struct ofono_emulator *em,
struct ofono_emulator_request *req, void *userdata)
{
const char *gain = "MicrophoneVolume";
set_gain(em, req, userdata, gain);
}
static void sfos_bt_vgs_cb(struct ofono_emulator *em,
struct ofono_emulator_request *req, void *userdata)
{
const char *gain = "SpeakerVolume";
set_gain(em, req, userdata, gain);
}
void sfos_bt_cv_dbus_new(struct sfos_bt *sfos_bt)
{
DBusConnection *conn = ofono_dbus_get_connection();
struct ofono_modem *modem = sfos_bt->modem;
const char *path = ofono_modem_get_path(modem);
if (g_dbus_register_interface(conn, path,
SFOS_BT_DBUS_CV_INTERFACE, cv_methods,
cv_signals, NULL, sfos_bt, NULL)){
ofono_modem_add_interface(modem,SFOS_BT_DBUS_CV_INTERFACE);
return;
}
ofono_error("D-Bus register failed");
}
static void sfos_bt_remove_handler(struct ofono_emulator *em)
{
ofono_emulator_remove_handler(em, "+VGS");
ofono_emulator_remove_handler(em, "+VGM");
}
void sfos_bt_cv_dbus_free(struct sfos_bt *sfos_bt)
{
DBusConnection *conn = ofono_dbus_get_connection();
struct ofono_modem *modem = sfos_bt->modem;
const char *path = ofono_modem_get_path(modem);
ofono_modem_remove_interface(modem, SFOS_BT_DBUS_CV_INTERFACE);
g_dbus_unregister_interface(conn, path,
SFOS_BT_DBUS_CV_INTERFACE);
}
static void sfos_bt_emu_watch_cb(struct ofono_atom *atom,
enum ofono_atom_watch_condition cond,
void *data)
{
struct sfos_bt *sfos_bt = data;
if (cond == OFONO_ATOM_WATCH_CONDITION_REGISTERED){
sfos_bt->em = __ofono_atom_get_data(atom);
sfos_bt_cv_dbus_new(sfos_bt);
ofono_emulator_add_handler(sfos_bt->em, "+VGS",
sfos_bt_vgs_cb, sfos_bt, NULL);
ofono_emulator_add_handler(sfos_bt->em, "+VGM",
sfos_bt_vgm_cb, sfos_bt, NULL);
} else {
sfos_bt_cv_dbus_free(sfos_bt);
sfos_bt_remove_handler(sfos_bt->em);
sfos_bt->em = NULL;
}
}
static void sfos_bt_emu_watch_destroy(void *data)
{
struct sfos_bt *sfos_bt = data;
sfos_bt->emu_watch = 0;
}
static void sfos_bt_free(void *data)
{
struct sfos_bt *sfos_bt = data;
if (sfos_bt->emu_watch)
__ofono_modem_remove_atom_watch(sfos_bt->modem,
sfos_bt->emu_watch);
if (sfos_bt->em) {
sfos_bt_cv_dbus_free(sfos_bt);
sfos_bt_remove_handler(sfos_bt->em);
}
g_free(sfos_bt);
}
static gint sfos_bt_find_modem(gconstpointer listdata, gconstpointer modem)
{
const struct sfos_bt *sfos_bt = listdata;
return (sfos_bt->modem != modem);
}
static void modem_watch(struct ofono_modem *modem, gboolean added, void *user)
{
struct sfos_bt *sfos_bt;
DBG("modem: %p, added: %d", modem, added);
if (added) {
sfos_bt = g_new0(struct sfos_bt, 1);
modems = g_slist_append(modems, sfos_bt);
sfos_bt->emu_watch = __ofono_modem_add_atom_watch(modem,
OFONO_ATOM_TYPE_EMULATOR_HFP, sfos_bt_emu_watch_cb,
sfos_bt, sfos_bt_emu_watch_destroy);
sfos_bt->modem = modem;
} else {
GSList *link = g_slist_find_custom(modems, modem,
sfos_bt_find_modem);
if (link) {
sfos_bt_free(link->data);
modems = g_slist_delete_link(modems, link);
}
}
}
static void call_modemwatch(struct ofono_modem *modem, void *user)
{
modem_watch(modem, TRUE, user);
}
static int sfos_bt_init(void)
{
modemwatch_id = __ofono_modemwatch_add(modem_watch, NULL, NULL);
__ofono_modem_foreach(call_modemwatch, NULL);
return 0;
}
static void sfos_bt_exit(void)
{
DBG("");
__ofono_modemwatch_remove(modemwatch_id);
g_slist_free_full(modems, sfos_bt_free);
}
OFONO_PLUGIN_DEFINE(sfos_bt, "Sailfish OS Bluetooth Plugin", VERSION,
OFONO_PLUGIN_PRIORITY_DEFAULT,
sfos_bt_init, sfos_bt_exit)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,77 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2016-2017 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
* 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.
*/
#ifndef SAILFISH_MANAGER_DBUS_H
#define SAILFISH_MANAGER_DBUS_H
#include <sailfish_manager.h>
struct sailfish_manager_dbus;
enum sailfish_manager_dbus_block {
SAILFISH_MANAGER_DBUS_BLOCK_NONE = 0,
SAILFISH_MANAGER_DBUS_BLOCK_MODEM = 0x01,
SAILFISH_MANAGER_DBUS_BLOCK_IMEI = 0x02,
SAILFISH_MANAGER_DBUS_BLOCK_ALL = 0x03
};
enum sailfish_manager_dbus_signal {
SAILFISH_MANAGER_SIGNAL_NONE = 0,
SAILFISH_MANAGER_SIGNAL_VOICE_IMSI = 0x01,
SAILFISH_MANAGER_SIGNAL_DATA_IMSI = 0x02,
SAILFISH_MANAGER_SIGNAL_VOICE_PATH = 0x04,
SAILFISH_MANAGER_SIGNAL_DATA_PATH = 0x08,
SAILFISH_MANAGER_SIGNAL_ENABLED_SLOTS = 0x10,
SAILFISH_MANAGER_SIGNAL_MMS_IMSI = 0x20,
SAILFISH_MANAGER_SIGNAL_MMS_PATH = 0x40,
SAILFISH_MANAGER_SIGNAL_READY = 0x80
};
/* Functionality provided by sailfish_manager to sailfish_manager_dbus */
struct sailfish_manager_dbus_cb {
GHashTable *(*get_errors)(struct sailfish_manager *m);
GHashTable *(*get_slot_errors)(const struct sailfish_slot *s);
void (*set_enabled_slots)(struct sailfish_manager *m, char **slots);
gboolean (*set_mms_imsi)(struct sailfish_manager *m, const char *imsi);
void (*set_default_voice_imsi)(struct sailfish_manager *m,
const char *imsi);
void (*set_default_data_imsi)(struct sailfish_manager *m,
const char *imsi);
};
struct sailfish_manager_dbus *sailfish_manager_dbus_new
(struct sailfish_manager *m,
const struct sailfish_manager_dbus_cb *cb);
void sailfish_manager_dbus_free(struct sailfish_manager_dbus *d);
void sailfish_manager_dbus_set_block(struct sailfish_manager_dbus *d,
enum sailfish_manager_dbus_block b);
void sailfish_manager_dbus_signal(struct sailfish_manager_dbus *d,
enum sailfish_manager_dbus_signal m);
void sailfish_manager_dbus_signal_sim(struct sailfish_manager_dbus *d,
int index, gboolean present);
void sailfish_manager_dbus_signal_error(struct sailfish_manager_dbus *d,
const char *id, const char *message);
void sailfish_manager_dbus_signal_modem_error(struct sailfish_manager_dbus *d,
int index, const char *id, const char *msg);
#endif /* SAILFISH_MANAGER_DBUS_H */
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -0,0 +1,614 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 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
* 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 "sailfish_sim_info.h"
#include "sailfish_watch.h"
#include <gutil_misc.h>
#include <gutil_log.h>
#include "ofono.h"
#include "common.h"
#include "storage.h"
#define SAILFISH_SIM_INFO_STORE "cache"
#define SAILFISH_SIM_INFO_STORE_GROUP "sim"
#define SAILFISH_SIM_INFO_STORE_SPN "spn"
/* ICCID -> IMSI map */
#define SAILFISH_SIM_ICCID_MAP "iccidmap"
#define SAILFISH_SIM_ICCID_MAP_IMSI "imsi"
#define DEFAULT_SPN_BUFSIZE 8
G_STATIC_ASSERT(DEFAULT_SPN_BUFSIZE >= \
OFONO_MAX_MCC_LENGTH + OFONO_MAX_MNC_LENGTH + 1);
typedef GObjectClass SailfishSimInfoClass;
typedef struct sailfish_sim_info SailfishSimInfo;
enum sailfish_watch_events {
WATCH_EVENT_SIM,
WATCH_EVENT_SIM_STATE,
WATCH_EVENT_ICCID,
WATCH_EVENT_IMSI,
WATCH_EVENT_SPN,
WATCH_EVENT_NETREG,
WATCH_EVENT_COUNT
};
struct sailfish_sim_info_priv {
struct sailfish_watch *watch;
struct ofono_netreg *netreg;
char *iccid;
char *imsi;
char *cached_spn;
char *sim_spn;
char *public_spn;
char default_spn[DEFAULT_SPN_BUFSIZE];
gulong watch_event_id[WATCH_EVENT_COUNT];
guint netreg_status_watch_id;
gboolean update_imsi_cache;
gboolean update_iccid_map;
};
enum sailfish_sim_info_signal {
SIGNAL_ICCID_CHANGED,
SIGNAL_IMSI_CHANGED,
SIGNAL_SPN_CHANGED,
SIGNAL_COUNT
};
#define SIGNAL_ICCID_CHANGED_NAME "sailfish-siminfo-iccid-changed"
#define SIGNAL_IMSI_CHANGED_NAME "sailfish-siminfo-imsi-changed"
#define SIGNAL_SPN_CHANGED_NAME "sailfish-siminfo-spn-changed"
static guint sailfish_sim_info_signals[SIGNAL_COUNT] = { 0 };
G_DEFINE_TYPE(SailfishSimInfo, sailfish_sim_info, G_TYPE_OBJECT)
#define SAILFISH_SIMINFO_TYPE (sailfish_sim_info_get_type())
#define SAILFISH_SIMINFO(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\
SAILFISH_SIMINFO_TYPE, SailfishSimInfo))
#define NEW_SIGNAL(klass,name) \
sailfish_sim_info_signals[SIGNAL_##name##_CHANGED] = \
g_signal_new(SIGNAL_##name##_CHANGED_NAME, \
G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST, \
0, NULL, NULL, NULL, G_TYPE_NONE, 0)
/* Skip the leading slash from the modem path: */
#define DBG_(obj,fmt,args...) DBG("%s " fmt, (obj)->path+1, ##args)
static void sailfish_sim_info_signal_emit(struct sailfish_sim_info *self,
enum sailfish_sim_info_signal id)
{
g_signal_emit(self, sailfish_sim_info_signals[id], 0);
}
static void sailfish_sim_info_update_imsi_cache(struct sailfish_sim_info *self)
{
struct sailfish_sim_info_priv *priv = self->priv;
if (priv->update_imsi_cache && priv->imsi && priv->imsi[0] &&
priv->cached_spn && priv->cached_spn[0]) {
gboolean save = FALSE;
const char *store = SAILFISH_SIM_INFO_STORE;
GKeyFile *cache = storage_open(priv->imsi, store);
char *spn = g_key_file_get_string(cache,
SAILFISH_SIM_INFO_STORE_GROUP,
SAILFISH_SIM_INFO_STORE_SPN, NULL);
if (g_strcmp0(priv->cached_spn, spn)) {
save = TRUE;
g_key_file_set_string(cache,
SAILFISH_SIM_INFO_STORE_GROUP,
SAILFISH_SIM_INFO_STORE_SPN,
priv->cached_spn);
}
/*
* Since we are most likely running on flash which
* supports a limited number of writes, don't overwrite
* the file unless something has actually changed.
*/
if (save) {
DBG_(self, "updating " STORAGEDIR "/%s/%s",
priv->imsi, store);
storage_close(priv->imsi, store, cache, TRUE);
} else {
g_key_file_free(cache);
}
g_free(spn);
priv->update_imsi_cache = FALSE;
}
}
static void sailfish_sim_info_update_iccid_map(struct sailfish_sim_info *self)
{
struct sailfish_sim_info_priv *priv = self->priv;
if (priv->update_iccid_map && priv->iccid && priv->iccid[0] &&
priv->imsi && priv->imsi[0]) {
const char *store = SAILFISH_SIM_ICCID_MAP;
GKeyFile *map = storage_open(NULL, store);
char *imsi = g_key_file_get_string(map,
SAILFISH_SIM_ICCID_MAP_IMSI, priv->iccid, NULL);
/*
* Since we are most likely running on flash which
* supports a limited number of writes, don't overwrite
* the file unless something has actually changed.
*/
if (g_strcmp0(imsi, priv->imsi)) {
DBG_(self, "updating " STORAGEDIR "/%s", store);
g_key_file_set_string(map, SAILFISH_SIM_ICCID_MAP_IMSI,
priv->iccid, priv->imsi);
storage_close(NULL, store, map, TRUE);
} else {
g_key_file_free(map);
}
g_free(imsi);
priv->update_iccid_map = FALSE;
}
}
static void sailfish_sim_info_update_public_spn(struct sailfish_sim_info *self)
{
struct sailfish_sim_info_priv *priv = self->priv;
const char *spn = priv->sim_spn ? priv->sim_spn :
priv->cached_spn ? priv->cached_spn :
priv->default_spn;
if (g_strcmp0(priv->public_spn, spn)) {
g_free(priv->public_spn);
self->spn = priv->public_spn = g_strdup(spn);
sailfish_sim_info_signal_emit(self, SIGNAL_SPN_CHANGED);
}
}
static void sailfish_sim_info_set_cached_spn(struct sailfish_sim_info *self,
const char *spn)
{
struct sailfish_sim_info_priv *priv = self->priv;
if (g_strcmp0(priv->cached_spn, spn)) {
g_free(priv->cached_spn);
if (spn) {
DBG_(self, "cached spn \"%s\"", spn);
priv->cached_spn = g_strdup(spn);
priv->update_imsi_cache = TRUE;
sailfish_sim_info_update_imsi_cache(self);
} else {
priv->cached_spn = NULL;
}
sailfish_sim_info_update_public_spn(self);
}
}
static void sailfish_sim_info_set_spn(struct sailfish_sim_info *self,
const char *spn)
{
struct sailfish_sim_info_priv *priv = self->priv;
if (g_strcmp0(priv->sim_spn, spn)) {
DBG_(self, "%s", spn);
g_free(priv->sim_spn);
priv->sim_spn = g_strdup(spn);
priv->update_imsi_cache = TRUE;
sailfish_sim_info_set_cached_spn(self, spn);
sailfish_sim_info_update_imsi_cache(self);
sailfish_sim_info_update_public_spn(self);
}
}
static void sailfish_sim_info_update_spn(struct sailfish_sim_info *self)
{
struct sailfish_watch *watch = self->priv->watch;
if (watch->spn && watch->spn[0]) {
sailfish_sim_info_set_spn(self, watch->spn);
}
}
static void sailfish_sim_info_update_default_spn(struct sailfish_sim_info *self)
{
struct sailfish_sim_info_priv *priv = self->priv;
struct ofono_sim *sim = priv->watch->sim;
char buf[DEFAULT_SPN_BUFSIZE];
const char *mcc = NULL;
const char *mnc = NULL;
if (sim && ofono_sim_get_state(sim) == OFONO_SIM_STATE_READY) {
mcc = ofono_sim_get_mcc(sim);
mnc = ofono_sim_get_mnc(sim);
}
if (mcc && mnc) {
snprintf(buf, DEFAULT_SPN_BUFSIZE, "%s%s", mcc, mnc);
buf[DEFAULT_SPN_BUFSIZE - 1] = 0;
} else {
buf[0] = 0;
}
if (strcmp(buf, priv->default_spn)) {
strncpy(priv->default_spn, buf, DEFAULT_SPN_BUFSIZE);
DBG_(self, "default spn \"%s\"", priv->default_spn);
sailfish_sim_info_update_public_spn(self);
}
}
static void sailfish_sim_info_set_imsi(struct sailfish_sim_info *self,
const char *imsi)
{
struct sailfish_sim_info_priv *priv = self->priv;
if (g_strcmp0(priv->imsi, imsi)) {
DBG_(self, "%s", imsi);
g_free(priv->imsi);
self->imsi = priv->imsi = g_strdup(imsi);
priv->update_iccid_map = TRUE;
sailfish_sim_info_update_iccid_map(self);
sailfish_sim_info_update_imsi_cache(self);
sailfish_sim_info_signal_emit(self, SIGNAL_IMSI_CHANGED);
}
}
static void sailfish_sim_info_update_imsi(struct sailfish_sim_info *self)
{
struct sailfish_watch *watch = self->priv->watch;
if (watch->imsi && watch->imsi[0]) {
sailfish_sim_info_set_imsi(self, watch->imsi);
}
}
static void sailfish_sim_info_network_check(struct sailfish_sim_info *self)
{
struct sailfish_sim_info_priv *priv = self->priv;
struct ofono_sim *sim = priv->watch->sim;
enum network_registration_status reg_status =
ofono_netreg_get_status(priv->netreg);
if (sim && ofono_sim_get_state(sim) == OFONO_SIM_STATE_READY &&
(reg_status == NETWORK_REGISTRATION_STATUS_REGISTERED ||
reg_status == NETWORK_REGISTRATION_STATUS_ROAMING)) {
const char *sim_mcc = ofono_sim_get_mcc(sim);
const char *sim_mnc = ofono_sim_get_mnc(sim);
const char *net_mcc = ofono_netreg_get_mcc(priv->netreg);
const char *net_mnc = ofono_netreg_get_mnc(priv->netreg);
const char *name = ofono_netreg_get_name(priv->netreg);
if (sim_mcc && sim_mcc[0] && sim_mnc && sim_mnc[0] &&
net_mcc && net_mcc[0] && net_mnc && net_mnc[0] &&
name && name[0] && !strcmp(sim_mcc, net_mcc) &&
!strcmp(sim_mnc, net_mnc)) {
/*
* If EFspn is present then sim_spn should be set
* before we get registered with the network.
*/
DBG_(self, "home network \"%s\"", name);
if (!priv->sim_spn) {
sailfish_sim_info_set_cached_spn(self, name);
}
}
}
}
static void sailfish_sim_info_load_cache(struct sailfish_sim_info *self)
{
struct sailfish_sim_info_priv *priv = self->priv;
if (priv->iccid && priv->iccid[0]) {
GKeyFile *map = storage_open(NULL, SAILFISH_SIM_ICCID_MAP);
char *imsi = g_key_file_get_string(map,
SAILFISH_SIM_ICCID_MAP_IMSI, priv->iccid, NULL);
g_key_file_free(map);
if (imsi && imsi[0] && g_strcmp0(priv->imsi, imsi)) {
if (priv->imsi && priv->imsi[0]) {
/* Need to update ICCID -> IMSI map */
DBG_(self, "IMSI changed %s -> %s",
priv->imsi, imsi);
priv->update_imsi_cache = TRUE;
}
g_free(priv->imsi);
self->imsi = priv->imsi = imsi;
DBG_(self, "imsi[%s] = %s", priv->iccid, imsi);
sailfish_sim_info_update_iccid_map(self);
sailfish_sim_info_signal_emit(self,
SIGNAL_IMSI_CHANGED);
} else if (imsi) {
g_free(imsi);
} else {
DBG_(self, "no imsi for iccid %s", priv->iccid);
}
}
if (priv->imsi && priv->imsi[0]) {
GKeyFile *cache = storage_open(priv->imsi,
SAILFISH_SIM_INFO_STORE);
char *spn = g_key_file_get_string(cache,
SAILFISH_SIM_INFO_STORE_GROUP,
SAILFISH_SIM_INFO_STORE_SPN, NULL);
g_key_file_free(cache);
if (spn && spn[0] && g_strcmp0(priv->cached_spn, spn)) {
if (priv->cached_spn && priv->cached_spn[0]) {
/* Need to update the cache file */
DBG_(self, "spn changing %s -> %s",
priv->cached_spn, spn);
priv->update_imsi_cache = TRUE;
}
g_free(priv->cached_spn);
priv->cached_spn = spn;
DBG_(self, "spn[%s] = \"%s\"", priv->imsi, spn);
sailfish_sim_info_update_imsi_cache(self);
sailfish_sim_info_update_public_spn(self);
} else if (spn) {
g_free(spn);
} else {
DBG_(self, "no spn for imsi %s", priv->imsi);
}
}
}
static void sailfish_sim_info_set_iccid(struct sailfish_sim_info *self,
const char *iccid)
{
struct sailfish_sim_info_priv *priv = self->priv;
if (g_strcmp0(priv->iccid, iccid)) {
g_free(priv->iccid);
self->iccid = priv->iccid = g_strdup(iccid);
sailfish_sim_info_signal_emit(self, SIGNAL_ICCID_CHANGED);
if (iccid) {
sailfish_sim_info_load_cache(self);
} else {
if (priv->imsi) {
g_free(priv->imsi);
self->imsi = priv->imsi = NULL;
sailfish_sim_info_signal_emit(self,
SIGNAL_IMSI_CHANGED);
}
if (priv->sim_spn) {
g_free(priv->sim_spn);
priv->sim_spn = NULL;
sailfish_sim_info_set_cached_spn(self, NULL);
}
}
}
}
static void sailfish_sim_info_sim_watch_cb(struct sailfish_watch *watch,
void *data)
{
struct sailfish_sim_info *self = SAILFISH_SIMINFO(data);
struct ofono_sim *sim = self->priv->watch->sim;
sailfish_sim_info_update_default_spn(self);
if (ofono_sim_get_state(sim) == OFONO_SIM_STATE_NOT_PRESENT) {
sailfish_sim_info_set_iccid(self, NULL);
}
sailfish_sim_info_network_check(self);
}
static void sailfish_sim_info_iccid_watch_cb(struct sailfish_watch *watch,
void *data)
{
struct sailfish_sim_info *self = SAILFISH_SIMINFO(data);
DBG_(self, "%s", watch->iccid);
sailfish_sim_info_set_iccid(self, watch->iccid);
}
static void sailfish_sim_info_imsi_watch_cb(struct sailfish_watch *watch,
void *data)
{
sailfish_sim_info_update_imsi(SAILFISH_SIMINFO(data));
}
static void sailfish_sim_info_spn_watch_cb(struct sailfish_watch *watch,
void *data)
{
sailfish_sim_info_update_spn(SAILFISH_SIMINFO(data));
}
static void sailfish_sim_info_netreg_watch(int status, int lac, int ci,
int tech, const char *mcc, const char *mnc, void *data)
{
sailfish_sim_info_network_check(SAILFISH_SIMINFO(data));
}
static void sailfish_sim_info_netreg_watch_done(void *data)
{
struct sailfish_sim_info *self = SAILFISH_SIMINFO(data);
struct sailfish_sim_info_priv *priv = self->priv;
GASSERT(priv->netreg_status_watch_id);
priv->netreg_status_watch_id = 0;
}
static void sailfish_sim_info_set_netreg(struct sailfish_sim_info *self,
struct ofono_netreg *netreg)
{
struct sailfish_sim_info_priv *priv = self->priv;
if (priv->netreg != netreg) {
if (netreg) {
DBG_(self, "netreg attached");
priv->netreg = netreg;
priv->netreg_status_watch_id =
__ofono_netreg_add_status_watch(netreg,
sailfish_sim_info_netreg_watch, self,
sailfish_sim_info_netreg_watch_done);
sailfish_sim_info_network_check(self);
} else if (priv->netreg) {
if (priv->netreg_status_watch_id) {
__ofono_netreg_remove_status_watch(priv->netreg,
priv->netreg_status_watch_id);
GASSERT(!priv->netreg_status_watch_id);
}
DBG_(self, "netreg detached");
priv->netreg = NULL;
}
}
}
static void sailfish_sim_info_netreg_changed(struct sailfish_watch *watch,
void *data)
{
sailfish_sim_info_set_netreg(SAILFISH_SIMINFO(data), watch->netreg);
}
struct sailfish_sim_info *sailfish_sim_info_new(const char *path)
{
struct sailfish_sim_info *self = NULL;
if (path) {
struct sailfish_watch *watch = sailfish_watch_new(path);
struct sailfish_sim_info_priv *priv;
self = g_object_new(SAILFISH_SIMINFO_TYPE, NULL);
priv = self->priv;
priv->watch = watch;
self->path = watch->path;
priv->watch_event_id[WATCH_EVENT_SIM] =
sailfish_watch_add_sim_changed_handler(watch,
sailfish_sim_info_sim_watch_cb, self);
priv->watch_event_id[WATCH_EVENT_SIM_STATE] =
sailfish_watch_add_sim_state_changed_handler(watch,
sailfish_sim_info_sim_watch_cb, self);
priv->watch_event_id[WATCH_EVENT_ICCID] =
sailfish_watch_add_iccid_changed_handler(watch,
sailfish_sim_info_iccid_watch_cb, self);
priv->watch_event_id[WATCH_EVENT_IMSI] =
sailfish_watch_add_imsi_changed_handler(watch,
sailfish_sim_info_imsi_watch_cb, self);
priv->watch_event_id[WATCH_EVENT_SPN] =
sailfish_watch_add_spn_changed_handler(watch,
sailfish_sim_info_spn_watch_cb, self);
priv->watch_event_id[WATCH_EVENT_NETREG] =
sailfish_watch_add_netreg_changed_handler(watch,
sailfish_sim_info_netreg_changed, self);
sailfish_sim_info_set_iccid(self, watch->iccid);
sailfish_sim_info_set_netreg(self, watch->netreg);
sailfish_sim_info_update_imsi(self);
sailfish_sim_info_update_spn(self);
sailfish_sim_info_network_check(self);
}
return self;
}
struct sailfish_sim_info *sailfish_sim_info_ref(struct sailfish_sim_info *self)
{
if (self) {
g_object_ref(SAILFISH_SIMINFO(self));
return self;
} else {
return NULL;
}
}
void sailfish_sim_info_unref(struct sailfish_sim_info *self)
{
if (self) {
g_object_unref(SAILFISH_SIMINFO(self));
}
}
void sailfish_sim_info_invalidate(struct sailfish_sim_info *self)
{
if (self) {
sailfish_sim_info_set_iccid(self, NULL);
}
}
gulong sailfish_sim_info_add_iccid_changed_handler(struct sailfish_sim_info *s,
sailfish_sim_info_cb_t cb, void *arg)
{
return (s && cb) ? g_signal_connect(s, SIGNAL_ICCID_CHANGED_NAME,
G_CALLBACK(cb), arg) : 0;
}
gulong sailfish_sim_info_add_imsi_changed_handler(struct sailfish_sim_info *s,
sailfish_sim_info_cb_t cb, void *arg)
{
return (s && cb) ? g_signal_connect(s, SIGNAL_IMSI_CHANGED_NAME,
G_CALLBACK(cb), arg) : 0;
}
gulong sailfish_sim_info_add_spn_changed_handler(struct sailfish_sim_info *s,
sailfish_sim_info_cb_t cb, void *arg)
{
return (s && cb) ? g_signal_connect(s, SIGNAL_SPN_CHANGED_NAME,
G_CALLBACK(cb), arg) : 0;
}
void sailfish_sim_info_remove_handler(struct sailfish_sim_info *s, gulong id)
{
if (s && id) {
g_signal_handler_disconnect(s, id);
}
}
void sailfish_sim_info_remove_handlers(struct sailfish_sim_info *self,
gulong *ids, int count)
{
gutil_disconnect_handlers(self, ids, count);
}
static void sailfish_sim_info_init(struct sailfish_sim_info *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, SAILFISH_SIMINFO_TYPE,
struct sailfish_sim_info_priv);
}
static void sailfish_sim_info_finalize(GObject *object)
{
struct sailfish_sim_info *self = SAILFISH_SIMINFO(object);
struct sailfish_sim_info_priv *priv = self->priv;
sailfish_watch_remove_all_handlers(priv->watch, priv->watch_event_id);
sailfish_watch_unref(priv->watch);
g_free(priv->iccid);
g_free(priv->imsi);
g_free(priv->sim_spn);
g_free(priv->cached_spn);
g_free(priv->public_spn);
G_OBJECT_CLASS(sailfish_sim_info_parent_class)->finalize(object);
}
static void sailfish_sim_info_class_init(SailfishSimInfoClass *klass)
{
G_OBJECT_CLASS(klass)->finalize = sailfish_sim_info_finalize;
g_type_class_add_private(klass, sizeof(struct sailfish_sim_info_priv));
NEW_SIGNAL(klass, ICCID);
NEW_SIGNAL(klass, IMSI);
NEW_SIGNAL(klass, SPN);
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -0,0 +1,81 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 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
* 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.
*/
#ifndef SAILFISH_SIM_INFO_H
#define SAILFISH_SIM_INFO_H
#include <ofono/types.h>
#include <glib.h>
#include <glib-object.h>
/*
* Note that iccid, imsi and spn provided by this class can be cached,
* i.e. become available before the pin code is entered and before those
* are known to the ofono core. That's the whole purpose of this thing.
*
* If you need to follow imsi known to the ofono core, you can use
* sailfish_sim_settings for that (or fight with ofono imsi watchers
* directly).
*/
struct ofono_modem;
struct sailfish_sim_info_priv;
struct sailfish_sim_info {
GObject object;
struct sailfish_sim_info_priv *priv;
const char *path;
const char *iccid;
const char *imsi;
const char *spn;
};
typedef void (*sailfish_sim_info_cb_t)(struct sailfish_sim_info *si,
void *user_data);
/* SIM info object associated with the particular slot */
struct sailfish_sim_info *sailfish_sim_info_new(const char *path);
struct sailfish_sim_info *sailfish_sim_info_ref(struct sailfish_sim_info *si);
void sailfish_sim_info_unref(struct sailfish_sim_info *si);
void sailfish_sim_info_invalidate(struct sailfish_sim_info *si);
gulong sailfish_sim_info_add_iccid_changed_handler(struct sailfish_sim_info *si,
sailfish_sim_info_cb_t cb, void *user_data);
gulong sailfish_sim_info_add_imsi_changed_handler(struct sailfish_sim_info *si,
sailfish_sim_info_cb_t cb, void *user_data);
gulong sailfish_sim_info_add_spn_changed_handler(struct sailfish_sim_info *si,
sailfish_sim_info_cb_t cb, void *user_data);
void sailfish_sim_info_remove_handler(struct sailfish_sim_info *si, gulong id);
void sailfish_sim_info_remove_handlers(struct sailfish_sim_info *si,
gulong *ids, int count);
#define sailfish_sim_info_remove_all_handlers(si,ids) \
sailfish_sim_info_remove_handlers(si, ids, G_N_ELEMENTS(ids))
/* And the D-Bus interface for it */
struct sailfish_sim_info_dbus;
struct sailfish_sim_info_dbus *sailfish_sim_info_dbus_new
(struct sailfish_sim_info *si);
struct sailfish_sim_info_dbus *sailfish_sim_info_dbus_new_path
(const char *path);
void sailfish_sim_info_dbus_free(struct sailfish_sim_info_dbus *dbus);
#endif /* SAILFISH_SIM_INFO_H */
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -0,0 +1,296 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 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
* 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 "sailfish_sim_info.h"
#include "sailfish_watch.h"
#include <ofono/dbus.h>
#include <gdbus.h>
#include "ofono.h"
enum watch_event_id {
WATCH_EVENT_MODEM,
WATCH_EVENT_COUNT
};
enum sim_info_event_id {
SIM_INFO_EVENT_ICCID,
SIM_INFO_EVENT_IMSI,
SIM_INFO_EVENT_SPN,
SIM_INFO_EVENT_COUNT
};
struct sailfish_sim_info_dbus {
struct sailfish_sim_info *info;
struct sailfish_watch *watch;
DBusConnection *conn;
gulong watch_event_id[WATCH_EVENT_COUNT];
gulong info_event_id[SIM_INFO_EVENT_COUNT];
};
#define SIM_INFO_DBUS_INTERFACE "org.nemomobile.ofono.SimInfo"
#define SIM_INFO_DBUS_INTERFACE_VERSION (1)
#define SIM_INFO_DBUS_ICCID_CHANGED_SIGNAL "CardIdentifierChanged"
#define SIM_INFO_DBUS_IMSI_CHANGED_SIGNAL "SubscriberIdentityChanged"
#define SIM_INFO_DBUS_SPN_CHANGED_SIGNAL "ServiceProviderNameChanged"
static void sailfish_sim_info_dbus_append_version(DBusMessageIter *it)
{
const dbus_int32_t version = SIM_INFO_DBUS_INTERFACE_VERSION;
dbus_message_iter_append_basic(it, DBUS_TYPE_INT32, &version);
}
static void sailfish_sim_info_dbus_append_string(DBusMessageIter *it,
const char *str)
{
if (!str) str = "";
dbus_message_iter_append_basic(it, DBUS_TYPE_STRING, &str);
}
static DBusMessage *sailfish_sim_info_dbus_reply_with_string(DBusMessage *msg,
const char *str)
{
DBusMessage *reply = dbus_message_new_method_return(msg);
DBusMessageIter iter;
dbus_message_iter_init_append(reply, &iter);
sailfish_sim_info_dbus_append_string(&iter, str);
return reply;
}
static DBusMessage *sailfish_sim_info_dbus_get_all(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct sailfish_sim_info_dbus *dbus = data;
struct sailfish_sim_info *info = dbus->info;
DBusMessage *reply = dbus_message_new_method_return(msg);
DBusMessageIter it;
dbus_message_iter_init_append(reply, &it);
sailfish_sim_info_dbus_append_version(&it);
sailfish_sim_info_dbus_append_string(&it, info->iccid);
sailfish_sim_info_dbus_append_string(&it, info->imsi);
sailfish_sim_info_dbus_append_string(&it, info->spn);
return reply;
}
static DBusMessage *sailfish_sim_info_dbus_get_version(DBusConnection *dc,
DBusMessage *msg, void *data)
{
DBusMessage *reply = dbus_message_new_method_return(msg);
DBusMessageIter it;
dbus_message_iter_init_append(reply, &it);
sailfish_sim_info_dbus_append_version(&it);
return reply;
}
static DBusMessage *sailfish_sim_info_dbus_get_iccid(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct sailfish_sim_info_dbus *dbus = data;
return sailfish_sim_info_dbus_reply_with_string(msg, dbus->info->iccid);
}
static DBusMessage *sailfish_sim_info_dbus_get_imsi(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct sailfish_sim_info_dbus *dbus = data;
return sailfish_sim_info_dbus_reply_with_string(msg, dbus->info->imsi);
}
static DBusMessage *sailfish_sim_info_dbus_get_spn(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct sailfish_sim_info_dbus *dbus = data;
return sailfish_sim_info_dbus_reply_with_string(msg, dbus->info->spn);
}
#define SIM_INFO_DBUS_VERSION_ARG {"version", "i"}
#define SIM_INFO_DBUS_ICCID_ARG {"iccid", "s"}
#define SIM_INFO_DBUS_IMSI_ARG {"imsi", "s"}
#define SIM_INFO_DBUS_SPN_ARG {"spn" , "s"}
#define SIM_INFO_DBUS_GET_ALL_ARGS \
SIM_INFO_DBUS_VERSION_ARG, \
SIM_INFO_DBUS_ICCID_ARG, \
SIM_INFO_DBUS_IMSI_ARG, \
SIM_INFO_DBUS_SPN_ARG
static const GDBusMethodTable sailfish_sim_info_dbus_methods[] = {
{ GDBUS_METHOD("GetAll",
NULL, GDBUS_ARGS(SIM_INFO_DBUS_GET_ALL_ARGS),
sailfish_sim_info_dbus_get_all) },
{ GDBUS_METHOD("GetInterfaceVersion",
NULL, GDBUS_ARGS(SIM_INFO_DBUS_VERSION_ARG),
sailfish_sim_info_dbus_get_version) },
{ GDBUS_METHOD("GetCardIdentifier",
NULL, GDBUS_ARGS(SIM_INFO_DBUS_ICCID_ARG),
sailfish_sim_info_dbus_get_iccid) },
{ GDBUS_METHOD("GetSubscriberIdentity",
NULL, GDBUS_ARGS(SIM_INFO_DBUS_IMSI_ARG),
sailfish_sim_info_dbus_get_imsi) },
{ GDBUS_METHOD("GetServiceProviderName",
NULL, GDBUS_ARGS(SIM_INFO_DBUS_SPN_ARG),
sailfish_sim_info_dbus_get_spn) },
{ }
};
static const GDBusSignalTable sailfish_sim_info_dbus_signals[] = {
{ GDBUS_SIGNAL(SIM_INFO_DBUS_ICCID_CHANGED_SIGNAL,
GDBUS_ARGS(SIM_INFO_DBUS_ICCID_ARG)) },
{ GDBUS_SIGNAL(SIM_INFO_DBUS_IMSI_CHANGED_SIGNAL,
GDBUS_ARGS(SIM_INFO_DBUS_IMSI_ARG)) },
{ GDBUS_SIGNAL(SIM_INFO_DBUS_SPN_CHANGED_SIGNAL,
GDBUS_ARGS(SIM_INFO_DBUS_SPN_ARG)) },
{ }
};
static void sailfish_sim_info_dbus_modem_cb(struct sailfish_watch *watch,
void *data)
{
if (watch->modem) {
ofono_modem_add_interface(watch->modem,
SIM_INFO_DBUS_INTERFACE);
}
}
static void sailfish_sim_info_dbus_emit(struct sailfish_sim_info_dbus *dbus,
const char *signal, const char *value)
{
const char *arg = value;
if (!arg) arg = "";
g_dbus_emit_signal(dbus->conn, dbus->info->path,
SIM_INFO_DBUS_INTERFACE, signal,
DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID);
}
static void sailfish_sim_info_dbus_iccid_cb(struct sailfish_sim_info *info,
void *data)
{
sailfish_sim_info_dbus_emit((struct sailfish_sim_info_dbus *)data,
SIM_INFO_DBUS_ICCID_CHANGED_SIGNAL, info->iccid);
}
static void sailfish_sim_info_dbus_imsi_cb(struct sailfish_sim_info *info,
void *data)
{
sailfish_sim_info_dbus_emit((struct sailfish_sim_info_dbus *)data,
SIM_INFO_DBUS_IMSI_CHANGED_SIGNAL, info->imsi);
}
static void sailfish_sim_info_dbus_spn_cb(struct sailfish_sim_info *info,
void *data)
{
sailfish_sim_info_dbus_emit((struct sailfish_sim_info_dbus *)data,
SIM_INFO_DBUS_SPN_CHANGED_SIGNAL, info->spn);
}
struct sailfish_sim_info_dbus *sailfish_sim_info_dbus_new
(struct sailfish_sim_info *info)
{
struct sailfish_sim_info_dbus *dbus =
g_slice_new0(struct sailfish_sim_info_dbus);
DBG("%s", info->path);
dbus->info = sailfish_sim_info_ref(info);
dbus->watch = sailfish_watch_new(info->path);
dbus->conn = dbus_connection_ref(ofono_dbus_get_connection());
/* Register D-Bus interface */
if (g_dbus_register_interface(dbus->conn, dbus->info->path,
SIM_INFO_DBUS_INTERFACE,
sailfish_sim_info_dbus_methods,
sailfish_sim_info_dbus_signals,
NULL, dbus, NULL)) {
if (dbus->watch->modem) {
ofono_modem_add_interface(dbus->watch->modem,
SIM_INFO_DBUS_INTERFACE);
}
dbus->watch_event_id[WATCH_EVENT_MODEM] =
sailfish_watch_add_modem_changed_handler(dbus->watch,
sailfish_sim_info_dbus_modem_cb, dbus);
dbus->info_event_id[SIM_INFO_EVENT_ICCID] =
sailfish_sim_info_add_iccid_changed_handler(info,
sailfish_sim_info_dbus_iccid_cb, dbus);
dbus->info_event_id[SIM_INFO_EVENT_IMSI] =
sailfish_sim_info_add_imsi_changed_handler(info,
sailfish_sim_info_dbus_imsi_cb, dbus);
dbus->info_event_id[SIM_INFO_EVENT_SPN] =
sailfish_sim_info_add_spn_changed_handler(info,
sailfish_sim_info_dbus_spn_cb, dbus);
return dbus;
} else {
ofono_error("SimInfo D-Bus register failed");
sailfish_sim_info_dbus_free(dbus);
return NULL;
}
}
struct sailfish_sim_info_dbus *sailfish_sim_info_dbus_new_path
(const char *path)
{
struct sailfish_sim_info_dbus *dbus = NULL;
struct sailfish_sim_info *info = sailfish_sim_info_new(path);
if (info) {
dbus = sailfish_sim_info_dbus_new(info);
sailfish_sim_info_unref(info);
}
return dbus;
}
void sailfish_sim_info_dbus_free(struct sailfish_sim_info_dbus *dbus)
{
if (dbus) {
DBG("%s", dbus->info->path);
g_dbus_unregister_interface(dbus->conn, dbus->info->path,
SIM_INFO_DBUS_INTERFACE);
if (dbus->watch->modem) {
ofono_modem_remove_interface(dbus->watch->modem,
SIM_INFO_DBUS_INTERFACE);
}
dbus_connection_unref(dbus->conn);
sailfish_watch_remove_all_handlers(dbus->watch,
dbus->watch_event_id);
sailfish_watch_unref(dbus->watch);
sailfish_sim_info_remove_all_handlers(dbus->info,
dbus->info_event_id);
sailfish_sim_info_unref(dbus->info);
g_slice_free(struct sailfish_sim_info_dbus, dbus);
}
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -0,0 +1,680 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 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
* 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 "sailfish_watch.h"
#include <gutil_misc.h>
#include <gutil_log.h>
#include "ofono.h"
typedef GObjectClass SailfishWatchClass;
typedef struct sailfish_watch SailfishWatch;
struct sailfish_watch_priv {
char *path;
char *iccid;
char *imsi;
char *spn;
int signals_suspended;
int queued_signals;
guint modem_watch_id;
guint online_watch_id;
guint sim_watch_id;
guint sim_state_watch_id;
guint iccid_watch_id;
guint imsi_watch_id;
guint spn_watch_id;
guint netreg_watch_id;
};
enum sailfish_watch_signal {
SIGNAL_MODEM_CHANGED,
SIGNAL_ONLINE_CHANGED,
SIGNAL_SIM_CHANGED,
SIGNAL_SIM_STATE_CHANGED,
SIGNAL_ICCID_CHANGED,
SIGNAL_IMSI_CHANGED,
SIGNAL_SPN_CHANGED,
SIGNAL_NETREG_CHANGED,
SIGNAL_COUNT
};
#define SIGNAL_MODEM_CHANGED_NAME "sailfish-watch-modem-changed"
#define SIGNAL_ONLINE_CHANGED_NAME "sailfish-watch-online-changed"
#define SIGNAL_SIM_CHANGED_NAME "sailfish-watch-sim-changed"
#define SIGNAL_SIM_STATE_CHANGED_NAME "sailfish-watch-sim-state-changed"
#define SIGNAL_ICCID_CHANGED_NAME "sailfish-watch-iccid-changed"
#define SIGNAL_IMSI_CHANGED_NAME "sailfish-watch-imsi-changed"
#define SIGNAL_SPN_CHANGED_NAME "sailfish-watch-spn-changed"
#define SIGNAL_NETREG_CHANGED_NAME "sailfish-watch-netreg-changed"
static guint sailfish_watch_signals[SIGNAL_COUNT] = { 0 };
static GHashTable* sailfish_watch_table = NULL;
G_DEFINE_TYPE(SailfishWatch, sailfish_watch, G_TYPE_OBJECT)
#define SAILFISH_WATCH_TYPE (sailfish_watch_get_type())
#define SAILFISH_WATCH(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\
SAILFISH_WATCH_TYPE, SailfishWatch))
#define NEW_SIGNAL(klass,name) \
sailfish_watch_signals[SIGNAL_##name##_CHANGED] = \
g_signal_new(SIGNAL_##name##_CHANGED_NAME, \
G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST, \
0, NULL, NULL, NULL, G_TYPE_NONE, 0)
/* Skip the leading slash from the modem path: */
#define DBG_(obj,fmt,args...) DBG("%s " fmt, (obj)->path+1, ##args)
static inline int sailfish_watch_signal_bit(enum sailfish_watch_signal id)
{
return (1 << id);
}
static inline void sailfish_watch_signal_emit(struct sailfish_watch *self,
enum sailfish_watch_signal id)
{
self->priv->queued_signals &= ~sailfish_watch_signal_bit(id);
g_signal_emit(self, sailfish_watch_signals[id], 0);
}
static inline void sailfish_watch_signal_queue(struct sailfish_watch *self,
enum sailfish_watch_signal id)
{
self->priv->queued_signals |= sailfish_watch_signal_bit(id);
}
static void sailfish_watch_emit_queued_signals(struct sailfish_watch *self)
{
struct sailfish_watch_priv *priv = self->priv;
if (priv->signals_suspended < 1) {
int i;
for (i = 0; priv->queued_signals && i < SIGNAL_COUNT; i++) {
if (priv->queued_signals &
sailfish_watch_signal_bit(i)) {
sailfish_watch_signal_emit(self, i);
}
}
}
}
static inline void sailfish_watch_suspend_signals(struct sailfish_watch *self)
{
self->priv->signals_suspended++;
}
static inline void sailfish_watch_resume_signals(struct sailfish_watch *self)
{
struct sailfish_watch_priv *priv = self->priv;
GASSERT(priv->signals_suspended > 0);
priv->signals_suspended--;
sailfish_watch_emit_queued_signals(self);
}
static void sailfish_watch_sim_state_notify(enum ofono_sim_state new_state,
void *user_data)
{
struct sailfish_watch *self = SAILFISH_WATCH(user_data);
sailfish_watch_signal_queue(self, SIGNAL_SIM_STATE_CHANGED);
sailfish_watch_emit_queued_signals(self);
}
static void sailfish_watch_sim_state_destroy(void *user_data)
{
struct sailfish_watch *self = SAILFISH_WATCH(user_data);
struct sailfish_watch_priv *priv = self->priv;
GASSERT(priv->sim_state_watch_id);
priv->sim_state_watch_id = 0;
}
static void sailfish_watch_iccid_update(struct sailfish_watch *self,
const char *iccid)
{
struct sailfish_watch_priv *priv = self->priv;
if (g_strcmp0(priv->iccid, iccid)) {
g_free(priv->iccid);
self->iccid = priv->iccid = g_strdup(iccid);
sailfish_watch_signal_queue(self, SIGNAL_ICCID_CHANGED);
}
}
static void sailfish_watch_iccid_notify(const char *iccid, void *user_data)
{
struct sailfish_watch *self = SAILFISH_WATCH(user_data);
sailfish_watch_iccid_update(self, iccid);
sailfish_watch_emit_queued_signals(self);
}
static void sailfish_watch_iccid_destroy(void *user_data)
{
struct sailfish_watch *self = SAILFISH_WATCH(user_data);
struct sailfish_watch_priv *priv = self->priv;
GASSERT(priv->iccid_watch_id);
priv->iccid_watch_id = 0;
}
static void sailfish_watch_spn_update(struct sailfish_watch *self,
const char *spn)
{
struct sailfish_watch_priv *priv = self->priv;
if (g_strcmp0(priv->spn, spn)) {
g_free(priv->spn);
self->spn = priv->spn = g_strdup(spn);
sailfish_watch_signal_queue(self, SIGNAL_SPN_CHANGED);
}
}
static void sailfish_watch_spn_notify(const char *spn, const char *dc,
void *user_data)
{
struct sailfish_watch *self = SAILFISH_WATCH(user_data);
sailfish_watch_spn_update(self, spn);
sailfish_watch_emit_queued_signals(self);
}
static void sailfish_watch_spn_destroy(void *user_data)
{
struct sailfish_watch *self = SAILFISH_WATCH(user_data);
struct sailfish_watch_priv *priv = self->priv;
GASSERT(priv->spn_watch_id);
priv->spn_watch_id = 0;
}
static void sailfish_watch_imsi_update(struct sailfish_watch *self,
const char *imsi)
{
struct sailfish_watch_priv *priv = self->priv;
if (g_strcmp0(priv->imsi, imsi)) {
g_free(priv->imsi);
self->imsi = priv->imsi = g_strdup(imsi);
sailfish_watch_signal_queue(self, SIGNAL_IMSI_CHANGED);
/* ofono core crashes if we add spn watch too early */
if (imsi) {
ofono_sim_add_spn_watch(self->sim, &priv->spn_watch_id,
sailfish_watch_spn_notify, self,
sailfish_watch_spn_destroy);
}
}
}
static void sailfish_watch_imsi_notify(const char *imsi, void *user_data)
{
struct sailfish_watch *self = SAILFISH_WATCH(user_data);
sailfish_watch_imsi_update(self, imsi);
sailfish_watch_emit_queued_signals(self);
}
static void sailfish_watch_imsi_destroy(void *user_data)
{
struct sailfish_watch *self = SAILFISH_WATCH(user_data);
struct sailfish_watch_priv *priv = self->priv;
GASSERT(priv->imsi_watch_id);
priv->imsi_watch_id = 0;
}
static void sailfish_watch_set_sim(struct sailfish_watch *self,
struct ofono_sim *sim)
{
if (self->sim != sim) {
struct sailfish_watch_priv *priv = self->priv;
if (priv->sim_state_watch_id) {
ofono_sim_remove_state_watch(self->sim,
priv->sim_state_watch_id);
/* The destroy callback clears it */
GASSERT(!priv->sim_state_watch_id);
}
if (priv->iccid_watch_id) {
ofono_sim_remove_iccid_watch(self->sim,
priv->iccid_watch_id);
/* The destroy callback clears it */
GASSERT(!priv->iccid_watch_id);
}
if (priv->imsi_watch_id) {
ofono_sim_remove_imsi_watch(self->sim,
priv->imsi_watch_id);
/* The destroy callback clears it */
GASSERT(!priv->imsi_watch_id);
}
if (priv->spn_watch_id) {
ofono_sim_remove_spn_watch(self->sim,
&priv->spn_watch_id);
/* The destroy callback clears it */
GASSERT(!priv->spn_watch_id);
}
self->sim = sim;
sailfish_watch_signal_queue(self, SIGNAL_SIM_CHANGED);
sailfish_watch_suspend_signals(self);
if (sim) {
priv->sim_state_watch_id =
ofono_sim_add_state_watch(sim,
sailfish_watch_sim_state_notify, self,
sailfish_watch_sim_state_destroy);
/*
* Unlike ofono_sim_add_state_watch, the rest
* of ofono_sim_add_xxx_watch functions call the
* notify callback if the value is already known
* to the ofono core.
*
* Also note that ofono core crashes if we add
* spn watch too early.
*/
priv->iccid_watch_id =
ofono_sim_add_iccid_watch(self->sim,
sailfish_watch_iccid_notify, self,
sailfish_watch_iccid_destroy);
priv->imsi_watch_id =
ofono_sim_add_imsi_watch(self->sim,
sailfish_watch_imsi_notify, self,
sailfish_watch_imsi_destroy);
} else {
/* And these will just queue the signals
* if necessary */
sailfish_watch_iccid_update(self, NULL);
sailfish_watch_imsi_update(self, NULL);
sailfish_watch_spn_update(self, NULL);
}
/* Emit the pending signals. */
sailfish_watch_resume_signals(self);
}
}
static void sailfish_watch_sim_notify(struct ofono_atom *atom,
enum ofono_atom_watch_condition cond, void *user_data)
{
struct sailfish_watch *self = SAILFISH_WATCH(user_data);
if (cond == OFONO_ATOM_WATCH_CONDITION_REGISTERED) {
struct ofono_sim *sim = __ofono_atom_get_data(atom);
DBG_(self, "sim registered");
sailfish_watch_set_sim(self, sim);
} else if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
DBG_(self, "sim unregistered");
sailfish_watch_set_sim(self, NULL);
}
}
static void sailfish_watch_sim_destroy(void *user_data)
{
struct sailfish_watch *self = SAILFISH_WATCH(user_data);
self->priv->sim_watch_id = 0;
}
static void sailfish_watch_set_netreg(struct sailfish_watch *self,
struct ofono_netreg *netreg)
{
if (self->netreg != netreg) {
self->netreg = netreg;
sailfish_watch_signal_emit(self, SIGNAL_NETREG_CHANGED);
}
}
static void sailfish_watch_netreg_notify(struct ofono_atom *atom,
enum ofono_atom_watch_condition cond, void *user_data)
{
struct sailfish_watch *self = SAILFISH_WATCH(user_data);
if (cond == OFONO_ATOM_WATCH_CONDITION_REGISTERED) {
struct ofono_netreg *netreg = __ofono_atom_get_data(atom);
DBG_(self, "netreg registered");
sailfish_watch_set_netreg(self, netreg);
} else if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
DBG_(self, "netreg unregistered");
sailfish_watch_set_netreg(self, NULL);
}
}
static void sailfish_watch_netreg_destroy(void *user_data)
{
struct sailfish_watch *self = SAILFISH_WATCH(user_data);
self->priv->netreg_watch_id = 0;
}
static void sailfish_watch_online_update(struct sailfish_watch *self,
gboolean online)
{
if (self->online != online) {
self->online = online;
sailfish_watch_signal_queue(self, SIGNAL_ONLINE_CHANGED);
}
}
static void sailfish_watch_online_notify(struct ofono_modem *modem,
ofono_bool_t online, void *user_data)
{
struct sailfish_watch *self = SAILFISH_WATCH(user_data);
GASSERT(self->modem == modem);
GASSERT(online == ofono_modem_get_online(modem));
sailfish_watch_online_update(self, online);
sailfish_watch_emit_queued_signals(self);
}
static void sailfish_watch_online_destroy(void *user_data)
{
struct sailfish_watch *self = SAILFISH_WATCH(user_data);
self->priv->online_watch_id = 0;
}
static void sailfish_watch_setup_modem(struct sailfish_watch *self)
{
struct sailfish_watch_priv *priv = self->priv;
GASSERT(!priv->online_watch_id);
priv->online_watch_id =
__ofono_modem_add_online_watch(self->modem,
sailfish_watch_online_notify, self,
sailfish_watch_online_destroy);
/* __ofono_modem_add_atom_watch() calls the notify callback if the
* atom is already registered */
GASSERT(!priv->sim_watch_id);
priv->sim_watch_id = __ofono_modem_add_atom_watch(self->modem,
OFONO_ATOM_TYPE_SIM, sailfish_watch_sim_notify,
self, sailfish_watch_sim_destroy);
GASSERT(!priv->netreg_watch_id);
priv->netreg_watch_id = __ofono_modem_add_atom_watch(self->modem,
OFONO_ATOM_TYPE_NETREG, sailfish_watch_netreg_notify,
self, sailfish_watch_netreg_destroy);
}
static void sailfish_watch_cleanup_modem(struct sailfish_watch *self,
struct ofono_modem *modem)
{
/* Caller checks the self->modem isn't NULL */
struct sailfish_watch_priv *priv = self->priv;
if (priv->online_watch_id) {
__ofono_modem_remove_online_watch(modem,
priv->online_watch_id);
GASSERT(!priv->online_watch_id);
}
if (priv->sim_watch_id) {
__ofono_modem_remove_atom_watch(modem, priv->sim_watch_id);
GASSERT(!priv->sim_watch_id);
}
if (priv->netreg_watch_id) {
__ofono_modem_remove_atom_watch(modem, priv->netreg_watch_id);
GASSERT(!priv->netreg_watch_id);
}
sailfish_watch_set_sim(self, NULL);
sailfish_watch_set_netreg(self, NULL);
}
static void sailfish_watch_set_modem(struct sailfish_watch *self,
struct ofono_modem *modem)
{
if (self->modem != modem) {
struct ofono_modem *old_modem = self->modem;
self->modem = modem;
sailfish_watch_signal_queue(self, SIGNAL_MODEM_CHANGED);
if (old_modem) {
sailfish_watch_cleanup_modem(self, old_modem);
}
if (modem) {
sailfish_watch_setup_modem(self);
}
sailfish_watch_emit_queued_signals(self);
}
}
static void sailfish_watch_modem_notify(struct ofono_modem *modem,
gboolean added, void *user_data)
{
struct sailfish_watch *self = SAILFISH_WATCH(user_data);
if (added) {
if (!g_strcmp0(self->path, ofono_modem_get_path(modem))) {
sailfish_watch_set_modem(self, modem);
}
} else if (self->modem == modem) {
sailfish_watch_set_modem(self, NULL);
}
}
static void sailfish_watch_modem_destroy(void *user_data)
{
struct sailfish_watch *self = SAILFISH_WATCH(user_data);
self->priv->modem_watch_id = 0;
}
static ofono_bool_t sailfish_watch_modem_find(struct ofono_modem *modem,
void *user_data)
{
struct sailfish_watch *self = SAILFISH_WATCH(user_data);
if (!g_strcmp0(self->path, ofono_modem_get_path(modem))) {
self->modem = modem;
sailfish_watch_setup_modem(self);
return TRUE;
} else {
return FALSE;
}
}
static void sailfish_watch_initialize(struct sailfish_watch *self,
const char *path)
{
struct sailfish_watch_priv *priv = self->priv;
self->path = priv->path = g_strdup(path);
ofono_modem_find(sailfish_watch_modem_find, self);
self->online = ofono_modem_get_online(self->modem);
priv->modem_watch_id =
__ofono_modemwatch_add(sailfish_watch_modem_notify, self,
sailfish_watch_modem_destroy);
}
static void sailfish_watch_destroyed(gpointer key, GObject* obj)
{
GASSERT(sailfish_watch_table);
DBG("%s", (char*)key);
if (sailfish_watch_table) {
GASSERT(g_hash_table_lookup(sailfish_watch_table, key) == obj);
g_hash_table_remove(sailfish_watch_table, key);
if (g_hash_table_size(sailfish_watch_table) == 0) {
g_hash_table_unref(sailfish_watch_table);
sailfish_watch_table = NULL;
}
}
}
struct sailfish_watch *sailfish_watch_new(const char *path)
{
struct sailfish_watch *watch = NULL;
if (path) {
if (sailfish_watch_table) {
watch = sailfish_watch_ref(g_hash_table_lookup(
sailfish_watch_table, path));
}
if (!watch) {
char* key = g_strdup(path);
watch = g_object_new(SAILFISH_WATCH_TYPE, NULL);
sailfish_watch_initialize(watch, path);
if (!sailfish_watch_table) {
/* Create the table on demand */
sailfish_watch_table =
g_hash_table_new_full(g_str_hash,
g_str_equal, g_free, NULL);
}
g_hash_table_replace(sailfish_watch_table, key, watch);
g_object_weak_ref(G_OBJECT(watch),
sailfish_watch_destroyed, key);
DBG_(watch, "created");
}
}
return watch;
}
struct sailfish_watch *sailfish_watch_ref(struct sailfish_watch *self)
{
if (self) {
g_object_ref(SAILFISH_WATCH(self));
return self;
} else {
return NULL;
}
}
void sailfish_watch_unref(struct sailfish_watch *self)
{
if (self) {
g_object_unref(SAILFISH_WATCH(self));
}
}
gulong sailfish_watch_add_modem_changed_handler(struct sailfish_watch *self,
sailfish_watch_cb_t cb, void *user_data)
{
return (self && cb) ? g_signal_connect(self,
SIGNAL_MODEM_CHANGED_NAME, G_CALLBACK(cb), user_data) : 0;
}
gulong sailfish_watch_add_online_changed_handler(struct sailfish_watch *self,
sailfish_watch_cb_t cb, void *user_data)
{
return (self && cb) ? g_signal_connect(self,
SIGNAL_ONLINE_CHANGED_NAME, G_CALLBACK(cb), user_data) : 0;
}
gulong sailfish_watch_add_sim_changed_handler(struct sailfish_watch *self,
sailfish_watch_cb_t cb, void *user_data)
{
return (self && cb) ? g_signal_connect(self,
SIGNAL_SIM_CHANGED_NAME, G_CALLBACK(cb), user_data) : 0;
}
gulong sailfish_watch_add_sim_state_changed_handler(struct sailfish_watch *self,
sailfish_watch_cb_t cb, void *user_data)
{
return (self && cb) ? g_signal_connect(self,
SIGNAL_SIM_STATE_CHANGED_NAME, G_CALLBACK(cb), user_data) : 0;
}
gulong sailfish_watch_add_iccid_changed_handler(struct sailfish_watch *self,
sailfish_watch_cb_t cb, void *user_data)
{
return (self && cb) ? g_signal_connect(self,
SIGNAL_ICCID_CHANGED_NAME, G_CALLBACK(cb), user_data) : 0;
}
gulong sailfish_watch_add_imsi_changed_handler(struct sailfish_watch *self,
sailfish_watch_cb_t cb, void *user_data)
{
return (self && cb) ? g_signal_connect(self,
SIGNAL_IMSI_CHANGED_NAME, G_CALLBACK(cb), user_data) : 0;
}
gulong sailfish_watch_add_spn_changed_handler(struct sailfish_watch *self,
sailfish_watch_cb_t cb, void *user_data)
{
return (self && cb) ? g_signal_connect(self,
SIGNAL_SPN_CHANGED_NAME, G_CALLBACK(cb), user_data) : 0;
}
gulong sailfish_watch_add_netreg_changed_handler(struct sailfish_watch *self,
sailfish_watch_cb_t cb, void *user_data)
{
return (self && cb) ? g_signal_connect(self,
SIGNAL_NETREG_CHANGED_NAME, G_CALLBACK(cb), user_data) : 0;
}
void sailfish_watch_remove_handler(struct sailfish_watch *self, gulong id)
{
if (self && id) {
g_signal_handler_disconnect(self, id);
}
}
void sailfish_watch_remove_handlers(struct sailfish_watch *self, gulong *ids,
int count)
{
gutil_disconnect_handlers(self, ids, count);
}
static void sailfish_watch_init(struct sailfish_watch *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, SAILFISH_WATCH_TYPE,
struct sailfish_watch_priv);
}
static void sailfish_watch_finalize(GObject *object)
{
struct sailfish_watch *self = SAILFISH_WATCH(object);
struct sailfish_watch_priv *priv = self->priv;
if (self->modem) {
struct ofono_modem *modem = self->modem;
self->modem = NULL;
sailfish_watch_cleanup_modem(self, modem);
}
if (priv->modem_watch_id) {
__ofono_modemwatch_remove(priv->modem_watch_id);
GASSERT(!priv->modem_watch_id);
}
g_free(priv->path);
G_OBJECT_CLASS(sailfish_watch_parent_class)->finalize(object);
}
static void sailfish_watch_class_init(SailfishWatchClass *klass)
{
G_OBJECT_CLASS(klass)->finalize = sailfish_watch_finalize;
g_type_class_add_private(klass, sizeof(struct sailfish_watch_priv));
NEW_SIGNAL(klass, MODEM);
NEW_SIGNAL(klass, ONLINE);
NEW_SIGNAL(klass, SIM);
NEW_SIGNAL(klass, SIM_STATE);
NEW_SIGNAL(klass, ICCID);
NEW_SIGNAL(klass, IMSI);
NEW_SIGNAL(klass, SPN);
NEW_SIGNAL(klass, NETREG);
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -0,0 +1,251 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
* Copyright (C) 2013-2017 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
* 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
#define _GNU_SOURCE
#include <errno.h>
#include <string.h>
#include <glib.h>
#define OFONO_API_SUBJECT_TO_CHANGE
#include <ofono/log.h>
#include <ofono/plugin.h>
#include <ofono/modem.h>
#include <ofono/gprs-provision.h>
#include "provision.h"
#include "mbpi.h"
struct provision_ap_defaults {
enum ofono_gprs_context_type type;
const char *name;
const char *apn;
};
static gint provision_match_strings(const char *s1, const char *s2)
{
gint match = 0;
/* Caller checks s2 for NULL */
if (s1) {
const gssize len1 = strlen(s1);
const gssize len2 = strlen(s2);
if (len1 == len2 && !strcmp(s1, s2)) {
/* Best match ever */
match = 3;
} else if (g_utf8_validate(s1, len1, NULL) &&
g_utf8_validate(s2, len2, NULL)) {
char *d1 = g_utf8_strdown(s1, len1);
char *d2 = g_utf8_strdown(s2, len2);
if (len1 == len2 && !strcmp(d1, d2)) {
/* Case insensitive match */
match = 2;
} else if ((len1 > len2 && strstr(d1, d2)) ||
(len2 > len1 && strstr(d2, d1))) {
/* Partial case insensitive match */
match = 1;
}
g_free(d1);
g_free(d2);
}
}
return match;
}
static gint provision_match_spn(const struct ofono_gprs_provision_data *ap,
const char *spn)
{
return provision_match_strings(ap->provider_name, spn) * 4 +
provision_match_strings(ap->name, spn);
}
static void provision_free_ap(gpointer data)
{
mbpi_ap_free(data);
}
static gint provision_compare_ap(gconstpointer a, gconstpointer b,
gpointer data)
{
const struct ofono_gprs_provision_data *ap1 = a;
const struct ofono_gprs_provision_data *ap2 = b;
const char *spn = data;
if (spn) {
const gint result = provision_match_spn(ap2, spn) -
provision_match_spn(ap1, spn);
if (result) {
return result;
}
}
if (ap1->provider_primary && !ap2->provider_primary) {
return -1;
} else if (ap2->provider_primary && !ap1->provider_primary) {
return 1;
}
return 0;
}
/* Picks best ap, deletes the rest. Creates one if necessary */
static GSList *provision_pick_best_ap(GSList *list, const char *spn,
const enum ofono_gprs_proto default_proto,
const struct provision_ap_defaults *defaults)
{
/* Sort the list */
list = g_slist_sort_with_data(list, provision_compare_ap, (void*)spn);
if (list) {
/* Pick the best one, delete the rest */
GSList *best = list;
g_slist_free_full(g_slist_remove_link(list, best),
provision_free_ap);
return best;
} else {
/* or create one from the default data */
struct ofono_gprs_provision_data *ap =
g_new0(struct ofono_gprs_provision_data, 1);
ap->proto = default_proto;
ap->type = defaults->type;
ap->name = g_strdup(defaults->name);
ap->apn = g_strdup(defaults->apn);
ap->auth_method = OFONO_GPRS_AUTH_METHOD_NONE;
return g_slist_append(NULL, ap);
}
}
/* Returns the list containing exactly one INTERNET and one MMS access point */
static GSList *provision_normalize_apn_list(GSList *apns, const char *spn)
{
static const struct provision_ap_defaults internet_defaults =
{ OFONO_GPRS_CONTEXT_TYPE_INTERNET, "Internet", "internet" };
static const struct provision_ap_defaults mms_defaults =
{ OFONO_GPRS_CONTEXT_TYPE_MMS, "MMS", "mms" };
GSList *internet_apns = NULL;
GSList *mms_apns = NULL;
/* Split internet and mms apns, delete all others */
while (apns) {
GSList *link = apns;
struct ofono_gprs_provision_data *ap = link->data;
apns = g_slist_remove_link(apns, link);
if (ap->type == OFONO_GPRS_CONTEXT_TYPE_INTERNET) {
internet_apns = g_slist_concat(internet_apns, link);
} else if (ap->type == OFONO_GPRS_CONTEXT_TYPE_MMS) {
mms_apns = g_slist_concat(mms_apns, link);
} else {
g_slist_free_full(link, provision_free_ap);
}
}
/* Pick the best ap of each type and concatenate them */
return g_slist_concat(
provision_pick_best_ap(internet_apns, spn,
mbpi_default_internet_proto, &internet_defaults),
provision_pick_best_ap(mms_apns, spn,
mbpi_default_mms_proto, &mms_defaults));
}
int provision_get_settings(const char *mcc, const char *mnc,
const char *spn,
struct ofono_gprs_provision_data **settings,
int *count)
{
GSList *l;
GSList *apns;
GError *error = NULL;
int ap_count;
int i;
ofono_info("Provisioning for MCC %s, MNC %s, SPN '%s'", mcc, mnc, spn);
/*
* Passing FALSE to mbpi_lookup_apn() would return
* an empty list if duplicates are found.
*/
apns = mbpi_lookup_apn(mcc, mnc, TRUE, &error);
if (error != NULL) {
ofono_error("%s", error->message);
g_error_free(error);
}
DBG("Found %d APs in MBPI", g_slist_length(apns));
apns = provision_normalize_apn_list(apns, spn);
ap_count = g_slist_length(apns);
DBG("Provisioning %d APs", ap_count);
*settings = g_new0(struct ofono_gprs_provision_data, ap_count);
*count = ap_count;
for (l = apns, i = 0; l; l = l->next, i++) {
struct ofono_gprs_provision_data *ap = l->data;
ofono_info("Name: '%s'", ap->name);
ofono_info(" APN: '%s'", ap->apn);
ofono_info(" Type: %s", mbpi_ap_type(ap->type));
ofono_info(" Username: '%s'", ap->username);
ofono_info(" Password: '%s'", ap->password);
memcpy(*settings + i, ap,
sizeof(struct ofono_gprs_provision_data));
g_free(ap);
}
g_slist_free(apns);
return 0;
}
static struct ofono_gprs_provision_driver provision_driver = {
.name = "Provisioning",
.get_settings = provision_get_settings
};
static int provision_init(void)
{
DBG("");
return ofono_gprs_provision_driver_register(&provision_driver);
}
static void provision_exit(void)
{
DBG("");
ofono_gprs_provision_driver_unregister(&provision_driver);
}
OFONO_PLUGIN_DEFINE(provision, "Provisioning Plugin", VERSION,
OFONO_PLUGIN_PRIORITY_DEFAULT,
provision_init, provision_exit)
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013-2014 Jolla Ltd.
* Copyright (C) 2013-2017 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
@@ -9,17 +9,13 @@
* 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 <glib.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <gutil_inotify.h>
#include <sys/inotify.h>
#include <wspcodec.h>
@@ -75,10 +71,7 @@ struct push_datagram_handler {
static GSList *handlers;
static GSList *modems;
static unsigned int modem_watch_id;
static int inotify_fd = -1;
static int inotify_watch_id = -1;
static guint inotify_watch_source_id;
static GIOChannel *inotify_watch_channel;
static GUtilInotifyWatchCallback *inotify_cb;
static void pf_notify_handler(struct push_datagram_handler *h,
const char *imsi, const char *from, const struct tm *remote,
@@ -277,10 +270,7 @@ static void pf_modem_watch(struct ofono_modem *modem,
if (added != FALSE) {
struct pf_modem *pm;
pm = g_try_new0(struct pf_modem, 1);
if (pm == NULL)
return;
pm = g_new0(struct pf_modem, 1);
pm->modem = modem;
pm->sms_watch_id = __ofono_modem_add_atom_watch(modem,
OFONO_ATOM_TYPE_SMS, pf_sms_watch, pm,
@@ -349,10 +339,7 @@ static void pf_parse_handler(GKeyFile *conf, const char *g)
if (path == NULL)
goto no_path;
h = g_try_new0(struct push_datagram_handler, 1);
if (h == NULL)
goto no_memory;
h = g_new0(struct push_datagram_handler, 1);
h->name = g_strdup(g);
h->interface = interface;
h->service = service;
@@ -385,9 +372,6 @@ static void pf_parse_handler(GKeyFile *conf, const char *g)
handlers = g_slist_append(handlers, h);
return;
no_memory:
g_free(path);
no_path:
g_free(method);
@@ -448,30 +432,11 @@ static void pf_parse_config(void)
g_dir_close(dir);
}
static gboolean pf_inotify(GIOChannel *gio, GIOCondition c, gpointer data)
static void pf_inotify(GUtilInotifyWatch *watch, guint mask, guint cookie,
const char *name, void *user_data)
{
int avail;
gsize len;
void *buf;
GError *error;
if (ioctl(inotify_fd, FIONREAD, &avail) < 0)
return FALSE;
buf = g_try_malloc(avail);
if (buf == NULL)
return FALSE;
error = NULL;
if (g_io_channel_read_chars(gio, buf, avail, &len, &error) !=
G_IO_STATUS_NORMAL) {
g_free(buf);
return FALSE;
}
DBG("'%s' changed (0x%04x)", name, mask);
pf_parse_config();
g_free(buf);
return TRUE;
}
static int pf_plugin_init(void)
@@ -480,37 +445,8 @@ static int pf_plugin_init(void)
pf_parse_config();
modem_watch_id = __ofono_modemwatch_add(pf_modem_watch, NULL, NULL);
__ofono_modem_foreach(pf_modem_init, NULL);
inotify_fd = inotify_init();
if (inotify_fd < 0)
return 0;
inotify_watch_id = inotify_add_watch(inotify_fd,
PF_CONFIG_DIR,
IN_CLOSE_WRITE | IN_DELETE | IN_MOVE);
if (inotify_watch_id < 0)
goto no_inotify_watch_id;
inotify_watch_channel = g_io_channel_unix_new(inotify_fd);
if (inotify_watch_channel == NULL)
goto no_inotify_watch_channel;
g_io_channel_set_encoding(inotify_watch_channel, NULL, NULL);
g_io_channel_set_buffered(inotify_watch_channel, FALSE);
inotify_watch_source_id = g_io_add_watch(inotify_watch_channel,
G_IO_IN, pf_inotify, NULL);
if (inotify_watch_source_id != 0)
return 0;
g_io_channel_unref(inotify_watch_channel);
inotify_watch_channel = NULL;
no_inotify_watch_channel:
inotify_rm_watch(inotify_fd, inotify_watch_id);
inotify_watch_id = -1;
no_inotify_watch_id:
close(inotify_fd);
inotify_fd = -1;
inotify_cb = gutil_inotify_watch_callback_new(PF_CONFIG_DIR,
IN_CLOSE_WRITE | IN_DELETE | IN_MOVE, pf_inotify, NULL);
return 0;
}
@@ -523,19 +459,18 @@ static void pf_plugin_exit(void)
modems = NULL;
g_slist_free_full(handlers, pf_free_handler);
handlers = NULL;
if (inotify_watch_source_id == 0)
return;
g_source_remove(inotify_watch_source_id);
inotify_watch_source_id = 0;
g_io_channel_unref(inotify_watch_channel);
inotify_watch_channel = NULL;
inotify_rm_watch(inotify_fd, inotify_watch_id);
inotify_watch_id = -1;
close(inotify_fd);
inotify_fd = -1;
gutil_inotify_watch_callback_free(inotify_cb);
inotify_cb = NULL;
}
OFONO_PLUGIN_DEFINE(push_forwarder, "Push Forwarder Plugin", VERSION,
OFONO_PLUGIN_DEFINE(pushforwarder, "Push Forwarder Plugin", VERSION,
OFONO_PLUGIN_PRIORITY_DEFAULT, pf_plugin_init,
pf_plugin_exit)
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -325,11 +325,14 @@ static gboolean setup_huawei(struct modem_info *modem)
if (g_strcmp0(info->label, "modem") == 0 ||
g_strcmp0(info->interface, "255/1/1") == 0 ||
g_strcmp0(info->interface, "255/2/1") == 0 ||
g_strcmp0(info->interface, "255/3/1") == 0 ||
g_strcmp0(info->interface, "255/1/49") == 0) {
mdm = info->devnode;
} else if (g_strcmp0(info->label, "pcui") == 0 ||
g_strcmp0(info->interface, "255/1/2") == 0 ||
g_strcmp0(info->interface, "255/2/2") == 0 ||
g_strcmp0(info->interface, "255/2/18") == 0 ||
g_strcmp0(info->interface, "255/3/18") == 0 ||
g_strcmp0(info->interface, "255/1/50") == 0) {
pcui = info->devnode;
} else if (g_strcmp0(info->label, "diag") == 0 ||

View File

@@ -34,6 +34,7 @@
#include <net/route.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <ctype.h>
#include <glib.h>
#include <gdbus.h>
@@ -258,6 +259,10 @@ static gboolean gprs_proto_from_string(const char *str,
static const char *gprs_auth_method_to_string(enum ofono_gprs_auth_method auth)
{
switch (auth) {
case OFONO_GPRS_AUTH_METHOD_ANY:
return "any";
case OFONO_GPRS_AUTH_METHOD_NONE:
return "none";
case OFONO_GPRS_AUTH_METHOD_CHAP:
return "chap";
case OFONO_GPRS_AUTH_METHOD_PAP:
@@ -276,6 +281,12 @@ static gboolean gprs_auth_method_from_string(const char *str,
} else if (g_str_equal(str, "pap")) {
*auth = OFONO_GPRS_AUTH_METHOD_PAP;
return TRUE;
} else if (g_str_equal(str, "any")) {
*auth = OFONO_GPRS_AUTH_METHOD_ANY;
return TRUE;
} else if (g_str_equal(str, "none")) {
*auth = OFONO_GPRS_AUTH_METHOD_NONE;
return TRUE;
}
return FALSE;
@@ -641,7 +652,48 @@ static gboolean pri_parse_proxy(struct pri_context *ctx, const char *proxy)
}
g_free(ctx->proxy_host);
ctx->proxy_host = g_strdup(host);
ctx->proxy_host = NULL;
if (host[0] == '0' || strstr(host, ".0")) {
/*
* Some operators provide IP address of the MMS proxy
* prepending zeros to each number shorter then 3 digits,
* e.g. "192.168.094.023" instead of "192.168.94.23".
* That may look nicer but it's actually wrong because
* the numbers starting with zeros are interpreted as
* octal numbers. In the example above 023 actually means
* 16 and 094 is not a valid number at all.
*
* In addition to publishing these broken settings on their
* web sites, some of the operators send them over the air,
* in which case we can't even blame the user for entering
* an invalid IP address. We better be prepared to deal with
* those.
*
* Since nobody in the world seems to be actually using the
* octal notation to write an IP address, let's remove the
* leading zeros if we find them in the host part of the MMS
* proxy URL.
*/
char** parts = g_strsplit(host, ".", -1);
guint count = g_strv_length(parts);
if (count == 4) {
char** ptr = parts;
while (*ptr) {
char* part = *ptr;
while (part[0] == '0' && isdigit(part[1])) {
memmove(part, part+1, strlen(part));
}
*ptr++ = part;
}
ctx->proxy_host = g_strjoinv(".", parts);
DBG("%s => %s", host, ctx->proxy_host);
}
g_strfreev(parts);
}
if (!ctx->proxy_host)
ctx->proxy_host = g_strdup(host);
g_free(scheme);
return TRUE;
@@ -892,6 +944,13 @@ static void pri_reset_context_properties(struct pri_context *ctx,
gprs_proto_to_string(ctx->context.proto));
}
if (ctx->context.auth_method != ap->auth_method) {
ctx->context.auth_method = ap->auth_method;
changed = TRUE;
pri_str_signal_change(ctx, "AuthenticationMethod",
gprs_auth_method_to_string(ctx->context.auth_method));
}
if (ap->type == OFONO_GPRS_CONTEXT_TYPE_MMS) {
if (pri_str_update(ctx->message_proxy, ap->message_proxy,
sizeof(ctx->message_proxy))) {
@@ -2274,6 +2333,11 @@ static DBusMessage *gprs_add_context(DBusConnection *conn,
const char *path;
enum ofono_gprs_context_type type;
#ifdef DISABLE_ADD_REMOVE_CONTEXT
ofono_error("AddContext not allowed");
return __ofono_error_not_supported(msg);
#endif
if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &typestr,
DBUS_TYPE_INVALID))
return __ofono_error_invalid_args(msg);
@@ -2355,6 +2419,11 @@ static DBusMessage *gprs_remove_context(DBusConnection *conn,
const char *path;
const char *atompath;
#ifdef DISABLE_ADD_REMOVE_CONTEXT
ofono_error("RemoveContext not allowed");
return __ofono_error_not_supported(msg);
#endif
if (gprs->pending)
return __ofono_error_busy(msg);

View File

@@ -203,7 +203,7 @@ static gboolean sco_accept(GIOChannel *io, GIOCondition cond,
send_new_connection(card->path, nsk, card->selected_codec);
close(nsk);
if (card->driver->sco_connected_hint)
if (card->driver && card->driver->sco_connected_hint)
card->driver->sco_connected_hint(card);
return TRUE;

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
* oFono - Open Source Telephony
*
* Copyright (C) 2016 Jolla Ltd.
* Copyright (C) 2016-2017 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,16 +13,23 @@
* GNU General Public License for more details.
*/
#include "ril_mtu.h"
#include "ril_log.h"
#include "mtu-watch.h"
#include <net/if.h>
#include <ofono/log.h>
#include <glib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <net/if.h>
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
struct ril_mtu_watch {
struct mtu_watch {
int max_mtu;
char *ifname;
void *buf;
@@ -32,7 +39,7 @@ struct ril_mtu_watch {
int fd;
};
static void ril_mtu_watch_limit_mtu(struct ril_mtu_watch *self)
static void mtu_watch_limit_mtu(struct mtu_watch *self)
{
int fd = socket(PF_INET, SOCK_DGRAM, 0);
if (fd >= 0) {
@@ -52,7 +59,7 @@ static void ril_mtu_watch_limit_mtu(struct ril_mtu_watch *self)
}
}
static void ril_mtu_watch_handle_rtattr(struct ril_mtu_watch *self,
static void mtu_watch_handle_rtattr(struct mtu_watch *self,
const struct rtattr *rta, int len)
{
int mtu = 0;
@@ -70,43 +77,43 @@ static void ril_mtu_watch_handle_rtattr(struct ril_mtu_watch *self,
}
if (mtu > self->max_mtu && !g_strcmp0(ifname, self->ifname)) {
DBG("%s %d", ifname, mtu);
ril_mtu_watch_limit_mtu(self);
mtu_watch_limit_mtu(self);
}
}
static void ril_mtu_watch_handle_ifinfomsg(struct ril_mtu_watch *self,
static void mtu_watch_handle_ifinfomsg(struct mtu_watch *self,
const struct ifinfomsg *ifi, int len)
{
if (ifi->ifi_flags & IFF_UP) {
const struct rtattr *rta = IFLA_RTA(ifi);
ril_mtu_watch_handle_rtattr(self, rta,
mtu_watch_handle_rtattr(self, rta,
len - ((char*)rta - (char*)ifi));
}
}
static void ril_mtu_watch_handle_nlmsg(struct ril_mtu_watch *self,
static void mtu_watch_handle_nlmsg(struct mtu_watch *self,
const struct nlmsghdr *hdr, int len)
{
while (len > 0 && NLMSG_OK(hdr, len)) {
if (hdr->nlmsg_type == RTM_NEWLINK) {
ril_mtu_watch_handle_ifinfomsg(self, NLMSG_DATA(hdr),
mtu_watch_handle_ifinfomsg(self, NLMSG_DATA(hdr),
IFLA_PAYLOAD(hdr));
}
hdr = NLMSG_NEXT(hdr, len);
}
}
static gboolean ril_mtu_watch_event(GIOChannel *ch, GIOCondition cond,
static gboolean mtu_watch_event(GIOChannel *ch, GIOCondition cond,
gpointer data)
{
struct ril_mtu_watch *self = data;
struct mtu_watch *self = data;
struct sockaddr_nl addr;
socklen_t addrlen = sizeof(addr);
ssize_t result = recvfrom(self->fd, self->buf, self->bufsize, 0,
(struct sockaddr *)&addr, &addrlen);
if (result > 0) {
if (!addr.nl_pid) {
ril_mtu_watch_handle_nlmsg(self, self->buf, result);
mtu_watch_handle_nlmsg(self, self->buf, result);
}
return G_SOURCE_CONTINUE;
} else if (result == 0 || errno == EINTR || errno == EAGAIN) {
@@ -118,9 +125,8 @@ static gboolean ril_mtu_watch_event(GIOChannel *ch, GIOCondition cond,
}
}
static gboolean ril_mtu_watch_open_socket(struct ril_mtu_watch *self)
static gboolean mtu_watch_open_socket(struct mtu_watch *self)
{
GASSERT(self->fd < 0);
self->fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
if (self->fd >= 0) {
struct sockaddr_nl nl;
@@ -140,20 +146,18 @@ static gboolean ril_mtu_watch_open_socket(struct ril_mtu_watch *self)
return FALSE;
}
static gboolean ril_mtu_watch_start(struct ril_mtu_watch *self)
static gboolean mtu_watch_start(struct mtu_watch *self)
{
if (self->fd >= 0) {
return TRUE;
} else if (ril_mtu_watch_open_socket(self)) {
GASSERT(!self->channel);
GASSERT(!self->io_watch);
} else if (mtu_watch_open_socket(self)) {
self->channel = g_io_channel_unix_new(self->fd);
if (self->channel) {
g_io_channel_set_encoding(self->channel, NULL, NULL);
g_io_channel_set_buffered(self->channel, FALSE);
self->io_watch = g_io_add_watch(self->channel,
G_IO_IN | G_IO_NVAL | G_IO_HUP,
ril_mtu_watch_event, self);
mtu_watch_event, self);
return TRUE;
}
close(self->fd);
@@ -162,7 +166,7 @@ static gboolean ril_mtu_watch_start(struct ril_mtu_watch *self)
return FALSE;
}
static void ril_mtu_watch_stop(struct ril_mtu_watch *self)
static void mtu_watch_stop(struct mtu_watch *self)
{
if (self->io_watch) {
g_source_remove(self->io_watch);
@@ -179,9 +183,9 @@ static void ril_mtu_watch_stop(struct ril_mtu_watch *self)
}
}
struct ril_mtu_watch *ril_mtu_watch_new(int max_mtu)
struct mtu_watch *mtu_watch_new(int max_mtu)
{
struct ril_mtu_watch *self = g_new0(struct ril_mtu_watch, 1);
struct mtu_watch *self = g_new0(struct mtu_watch, 1);
self->fd = -1;
self->max_mtu = max_mtu;
self->bufsize = 4096;
@@ -189,35 +193,27 @@ struct ril_mtu_watch *ril_mtu_watch_new(int max_mtu)
return self;
}
void ril_mtu_watch_free(struct ril_mtu_watch *self)
void mtu_watch_free(struct mtu_watch *self)
{
if (self) {
ril_mtu_watch_stop(self);
mtu_watch_stop(self);
g_free(self->ifname);
g_free(self->buf);
g_free(self);
}
}
void ril_mtu_watch_set_ifname(struct ril_mtu_watch *self, const char *ifname)
void mtu_watch_set_ifname(struct mtu_watch *self, const char *ifname)
{
if (self && g_strcmp0(self->ifname, ifname)) {
g_free(self->ifname);
if (ifname) {
self->ifname = g_strdup(ifname);
ril_mtu_watch_limit_mtu(self);
ril_mtu_watch_start(self);
mtu_watch_limit_mtu(self);
mtu_watch_start(self);
} else {
self->ifname = NULL;
ril_mtu_watch_stop(self);
mtu_watch_stop(self);
}
}
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -1,7 +1,7 @@
/*
* oFono - Open Source Telephony - RIL-based devices
* oFono - Open Source Telephony
*
* Copyright (C) 2016 Jolla Ltd.
* Copyright (C) 2016-2017 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,21 +13,13 @@
* GNU General Public License for more details.
*/
#ifndef RIL_MTU_H
#define RIL_MTU_H
#ifndef MTU_WATCH_H
#define MTU_WATCH_H
#include "ril_types.h"
struct mtu_watch;
struct ril_mtu_watch *ril_mtu_watch_new(int max_mtu);
void ril_mtu_watch_free(struct ril_mtu_watch *mw);
void ril_mtu_watch_set_ifname(struct ril_mtu_watch *mw, const char *ifname);
struct mtu_watch *mtu_watch_new(int max_mtu);
void mtu_watch_free(struct mtu_watch *mw);
void mtu_watch_set_ifname(struct mtu_watch *mw, const char *ifname);
#endif /* RIL_MTU_H */
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/
#endif /* MTU_WATCH_H */

View File

@@ -1828,6 +1828,17 @@ const char *ofono_netreg_get_mnc(struct ofono_netreg *netreg)
return netreg->current_operator->mnc;
}
const char *ofono_netreg_get_name(struct ofono_netreg *netreg)
{
if (netreg == NULL)
return NULL;
if (netreg->current_operator == NULL)
return NULL;
return netreg->current_operator->name;
}
struct sim_spdi *ofono_netreg_get_spdi(struct ofono_netreg *netreg)
{
if (netreg == NULL)

View File

@@ -386,6 +386,9 @@ void __ofono_sim_refresh(struct ofono_sim *sim, GSList *file_list,
void __ofono_sim_recheck_pin(struct ofono_sim *sim);
enum ofono_sim_password_type __ofono_sim_puk2pin(
enum ofono_sim_password_type type);
#include <ofono/stk.h>
typedef void (*__ofono_sms_sim_download_cb_t)(ofono_bool_t ok,

View File

@@ -889,9 +889,13 @@ static void radio_load_settings(struct ofono_radio_settings *rs,
"GsmBand", rs->band_gsm);
}
if (error) {
g_error_free(error);
error = NULL;
}
rs->pending_band_gsm = rs->band_gsm;
error = NULL;
rs->band_umts = g_key_file_get_integer(rs->settings, SETTINGS_GROUP,
"UmtsBand", &error);
@@ -901,9 +905,13 @@ static void radio_load_settings(struct ofono_radio_settings *rs,
"UmtsBand", rs->band_umts);
}
if (error) {
g_error_free(error);
error = NULL;
}
rs->pending_band_umts = rs->band_umts;
error = NULL;
rs->mode = g_key_file_get_integer(rs->settings, SETTINGS_GROUP,
"TechnologyPreference", &error);
@@ -913,6 +921,11 @@ static void radio_load_settings(struct ofono_radio_settings *rs,
"TechnologyPreference", rs->mode);
}
if (error) {
g_error_free(error);
error = NULL;
}
DBG("TechnologyPreference: %d", rs->mode);
DBG("GsmBand: %d", rs->band_gsm);
DBG("UmtsBand: %d", rs->band_umts);

View File

@@ -197,7 +197,10 @@ static gboolean password_is_pin(enum ofono_sim_password_type type)
return FALSE;
}
static enum ofono_sim_password_type puk2pin(enum ofono_sim_password_type type)
#define puk2pin(type) __ofono_sim_puk2pin(type)
enum ofono_sim_password_type __ofono_sim_puk2pin(
enum ofono_sim_password_type type)
{
switch (type) {
case OFONO_SIM_PASSWORD_SIM_PUK:
@@ -2511,13 +2514,6 @@ static void sim_spn_close(struct ofono_sim *sim)
static void sim_free_main_state(struct ofono_sim *sim)
{
int i;
for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++)
sim->pin_retries[i] = -1;
memset(sim->locked_pins, 0, sizeof(sim->locked_pins));
if (sim->imsi) {
g_free(sim->imsi);
sim->imsi = NULL;
@@ -2607,31 +2603,50 @@ static void sim_set_locked_pin(struct ofono_sim *sim,
g_strfreev(locked_pins);
}
static void sim_query_fac_imsilock_cb(const struct ofono_error *error,
static void sim_query_fac_pinlock_cb(const struct ofono_error *error,
ofono_bool_t status, void *data)
{
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
return;
struct ofono_sim *sim = data;
sim_set_locked_pin(data, OFONO_SIM_PASSWORD_PHSIM_PIN, status);
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
goto done;
sim_set_locked_pin(data, OFONO_SIM_PASSWORD_SIM_PIN, status);
done:
sim_initialize(sim);
}
static void sim_query_fac_networklock_cb(const struct ofono_error *error,
ofono_bool_t status, void *data)
{
struct ofono_sim *sim = data;
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
return;
goto done;
sim_set_locked_pin(data, OFONO_SIM_PASSWORD_PHNET_PIN, status);
done:
sim->driver->query_facility_lock(sim,
OFONO_SIM_PASSWORD_SIM_PIN,
sim_query_fac_pinlock_cb, sim);
}
static void sim_query_fac_pinlock_cb(const struct ofono_error *error,
static void sim_query_fac_imsilock_cb(const struct ofono_error *error,
ofono_bool_t status, void *data)
{
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
return;
struct ofono_sim *sim = data;
sim_set_locked_pin(data, OFONO_SIM_PASSWORD_SIM_PIN, status);
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
goto done;
sim_set_locked_pin(data, OFONO_SIM_PASSWORD_PHSIM_PIN, status);
done:
sim->driver->query_facility_lock(sim,
OFONO_SIM_PASSWORD_PHNET_PIN,
sim_query_fac_networklock_cb, sim);
}
void ofono_sim_inserted_notify(struct ofono_sim *sim, ofono_bool_t inserted)
@@ -2665,18 +2680,28 @@ void ofono_sim_inserted_notify(struct ofono_sim *sim, ofono_bool_t inserted)
OFONO_SIM_PASSWORD_PHSIM_PIN,
sim_query_fac_imsilock_cb, sim);
sim->driver->query_facility_lock(sim,
OFONO_SIM_PASSWORD_PHNET_PIN,
sim_query_fac_networklock_cb, sim);
sim->driver->query_facility_lock(sim,
OFONO_SIM_PASSWORD_SIM_PIN,
sim_query_fac_pinlock_cb, sim);
} else {
sim_initialize(sim);
}
} else {
switch (sim->pin_type) {
case OFONO_SIM_PASSWORD_SIM_PIN:
case OFONO_SIM_PASSWORD_SIM_PUK:
case OFONO_SIM_PASSWORD_SIM_PIN2:
case OFONO_SIM_PASSWORD_SIM_PUK2:
sim->pin_type = OFONO_SIM_PASSWORD_NONE;
break;
default:
break;
}
sim_initialize(sim);
} else {
sim->pin_type = OFONO_SIM_PASSWORD_NONE;
sim->locked_pins[OFONO_SIM_PASSWORD_SIM_PIN] = FALSE;
sim->locked_pins[OFONO_SIM_PASSWORD_SIM_PIN2] = FALSE;
sim->pin_retries[OFONO_SIM_PASSWORD_SIM_PIN] = -1;
sim->pin_retries[OFONO_SIM_PASSWORD_SIM_PUK] = -1;
sim->pin_retries[OFONO_SIM_PASSWORD_SIM_PIN2] = -1;
sim->pin_retries[OFONO_SIM_PASSWORD_SIM_PUK2] = -1;
sim_free_state(sim);
}
@@ -3282,8 +3307,6 @@ ofono_bool_t __ofono_is_valid_sim_pin(const char *pin,
return is_valid_pin(pin, 8, 8);
break;
case OFONO_SIM_PASSWORD_NONE:
return is_valid_pin(pin, 0, 8);
break;
case OFONO_SIM_PASSWORD_INVALID:
break;
}

View File

@@ -226,6 +226,9 @@ void sim_fs_notify_file_watches(struct sim_fs *fs, int id)
struct ofono_sim_context *context = l->data;
GSList *k;
if (context->file_watches == NULL)
continue;
for (k = context->file_watches->items; k; k = k->next) {
struct file_watch *w = k->data;
ofono_sim_file_changed_cb_t notify = w->item.notify;

View File

@@ -28,6 +28,11 @@
#include <fcntl.h>
#include <sys/types.h>
/* STORAGEDIR may need to be redefined in unit tests */
#ifndef STORAGEDIR
# define STORAGEDIR DEFAULT_STORAGEDIR
#endif
int create_dirs(const char *filename, const mode_t mode);
ssize_t read_file(unsigned char *buffer, size_t len,

View File

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

View File

@@ -3748,6 +3748,15 @@ int ofono_voicecall_get_next_callid(struct ofono_voicecall *vc)
return __ofono_modem_callid_next(modem);
}
struct ofono_call *ofono_voicecall_find_call(struct ofono_voicecall *vc,
unsigned int id)
{
GSList *l = g_slist_find_custom(vc->call_list, GUINT_TO_POINTER(id),
call_compare_by_id);
return l ? ((struct voicecall *)l->data)->call : NULL;
}
ofono_bool_t __ofono_voicecall_is_busy(struct ofono_voicecall *vc,
enum ofono_voicecall_interaction type)
{

15
ofono/test/cancel-sms Executable file
View File

@@ -0,0 +1,15 @@
#!/usr/bin/python3
import sys
import dbus
bus = dbus.SystemBus()
manager = dbus.Interface(bus.get_object('org.ofono', '/'),
'org.ofono.Manager')
path = sys.argv[1]
message = dbus.Interface(bus.get_object('org.ofono', path),
'org.ofono.Message')
message.Cancel()

18
ofono/test/disable-throttling Executable file
View File

@@ -0,0 +1,18 @@
#!/usr/bin/python3
import dbus, sys
bus = dbus.SystemBus()
if len(sys.argv) == 2:
path = sys.argv[1]
else:
manager = dbus.Interface(bus.get_object('org.ofono', '/'),
'org.ofono.Manager')
modems = manager.GetModems()
path = modems[0][0]
print("Disabling transmit power throttling of modem %s ..." % path)
thermal_management = dbus.Interface(bus.get_object('org.ofono', path),
'org.ofono.sofia3gr.ThermalManagement')
thermal_management.SetProperty("TransmitPowerThrottling", dbus.Boolean(0), timeout = 30)

18
ofono/test/enable-throttling Executable file
View File

@@ -0,0 +1,18 @@
#!/usr/bin/python3
import dbus, sys
bus = dbus.SystemBus()
if len(sys.argv) == 2:
path = sys.argv[1]
else:
manager = dbus.Interface(bus.get_object('org.ofono', '/'),
'org.ofono.Manager')
modems = manager.GetModems()
path = modems[0][0]
print("Enabling transmit power throttling of modem %s ..." % path)
thermal_management = dbus.Interface(bus.get_object('org.ofono', path),
'org.ofono.sofia3gr.ThermalManagement')
thermal_management.SetProperty("TransmitPowerThrottling", dbus.Boolean(1), timeout = 30)

0
ofono/test/get-serving-cell-info Normal file → Executable file
View File

58
ofono/unit/coverage Executable file
View File

@@ -0,0 +1,58 @@
#!/bin/bash
#
# Script to generate unit test coverage report, requires lcov:
#
# http://ltp.sourceforge.net/coverage/lcov.php
#
# Tests with coverage enabled:
TESTS="\
test-common \
test-util \
test-idmap \
test-simutil \
test-stkutil \
test-sms \
test-cdmasms \
test-sms-root \
test-caif \
test-provision \
test-sailfish_manager \
test-sailfish_sim_info"
pushd `dirname $0` > /dev/null
TEST_DIR="$PWD"
pushd .. > /dev/null
BASE_DIR="$PWD"
popd > /dev/null
popd > /dev/null
FULL_COV="$TEST_DIR/full.gcov"
PLUGINS_COV="$TEST_DIR/plugins.gcov"
SRC_COV="$TEST_DIR/src.gcov"
OUT="$TEST_DIR/html"
# Clean everything up
find "$BASE_DIR" -name "*.gcda" -exec rm {} \;
rm -f "$FULL_COV" "$PLUGINS_COV" "$SRC_COV"
rm -fr "$OUT"
# Run the tests
for t in $TESTS ; do
pushd "$TEST_DIR" > /dev/null
"$TEST_DIR/$t"
RC=$?
popd > /dev/null
[ $RC = 0 ] || exit 1
done
# LCOV 1.10 has branch coverage disabled per default
LCOV_OPT="--rc lcov_branch_coverage=1"
GENHTML_OPT="--branch-coverage"
lcov $LCOV_OPT -c -d "$BASE_DIR" -o "$FULL_COV" || exit 1
lcov $LCOV_OPT -e "$FULL_COV" "$BASE_DIR/plugins/*" -o "$PLUGINS_COV" || exit 1
lcov $LCOV_OPT -e "$FULL_COV" "$BASE_DIR/src/*" -o "$SRC_COV" || exit 1
genhtml $GENHTML_OPT -t ofono "$PLUGINS_COV" "$SRC_COV" --output-directory "$OUT" || exit 1
echo Coverage report: $OUT/index.html

View File

@@ -0,0 +1,326 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 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
* 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 "fake_sailfish_watch.h"
#include <gutil_misc.h>
#include <gutil_log.h>
#include "ofono.h"
typedef GObjectClass SailfishWatchClass;
typedef struct sailfish_watch SailfishWatch;
struct sailfish_watch_priv {
char *path;
char *iccid;
char *imsi;
char *spn;
int queued_signals;
};
#define SIGNAL_MODEM_CHANGED_NAME "sailfish-watch-modem-changed"
#define SIGNAL_ONLINE_CHANGED_NAME "sailfish-watch-online-changed"
#define SIGNAL_SIM_CHANGED_NAME "sailfish-watch-sim-changed"
#define SIGNAL_SIM_STATE_CHANGED_NAME "sailfish-watch-sim-state-changed"
#define SIGNAL_ICCID_CHANGED_NAME "sailfish-watch-iccid-changed"
#define SIGNAL_IMSI_CHANGED_NAME "sailfish-watch-imsi-changed"
#define SIGNAL_SPN_CHANGED_NAME "sailfish-watch-spn-changed"
#define SIGNAL_NETREG_CHANGED_NAME "sailfish-watch-netreg-changed"
static guint sailfish_watch_signals[WATCH_SIGNAL_COUNT] = { 0 };
static GHashTable* sailfish_watch_table = NULL;
G_DEFINE_TYPE(SailfishWatch, sailfish_watch, G_TYPE_OBJECT)
#define SAILFISH_WATCH_TYPE (sailfish_watch_get_type())
#define SAILFISH_WATCH(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\
SAILFISH_WATCH_TYPE, SailfishWatch))
#define NEW_SIGNAL(klass,name) \
sailfish_watch_signals[WATCH_SIGNAL_##name##_CHANGED] = \
g_signal_new(SIGNAL_##name##_CHANGED_NAME, \
G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST, \
0, NULL, NULL, NULL, G_TYPE_NONE, 0)
#define DBG_(obj,fmt,args...) DBG("%s " fmt, (obj)->path+1, ##args)
static inline int sailfish_watch_signal_bit(enum sailfish_watch_signal id)
{
return (1 << id);
}
static inline void sailfish_watch_signal_emit(struct sailfish_watch *self,
enum sailfish_watch_signal id)
{
self->priv->queued_signals &= ~sailfish_watch_signal_bit(id);
g_signal_emit(self, sailfish_watch_signals[id], 0);
}
void fake_sailfish_watch_signal_queue(struct sailfish_watch *self,
enum sailfish_watch_signal id)
{
self->priv->queued_signals |= sailfish_watch_signal_bit(id);
}
void fake_sailfish_watch_emit_queued_signals(struct sailfish_watch *self)
{
struct sailfish_watch_priv *priv = self->priv;
int i;
for (i = 0; priv->queued_signals && i < WATCH_SIGNAL_COUNT; i++) {
if (priv->queued_signals & sailfish_watch_signal_bit(i)) {
sailfish_watch_signal_emit(self, i);
}
}
}
void fake_sailfish_watch_set_ofono_iccid(struct sailfish_watch *self,
const char *iccid)
{
struct sailfish_watch_priv *priv = self->priv;
if (g_strcmp0(priv->iccid, iccid)) {
g_free(priv->iccid);
self->iccid = priv->iccid = g_strdup(iccid);
fake_sailfish_watch_signal_queue(self,
WATCH_SIGNAL_ICCID_CHANGED);
}
}
void fake_sailfish_watch_set_ofono_imsi(struct sailfish_watch *self,
const char *imsi)
{
struct sailfish_watch_priv *priv = self->priv;
if (g_strcmp0(priv->imsi, imsi)) {
g_free(priv->imsi);
self->imsi = priv->imsi = g_strdup(imsi);
fake_sailfish_watch_signal_queue(self,
WATCH_SIGNAL_IMSI_CHANGED);
}
}
void fake_sailfish_watch_set_ofono_spn(struct sailfish_watch *self,
const char *spn)
{
struct sailfish_watch_priv *priv = self->priv;
if (g_strcmp0(priv->spn, spn)) {
g_free(priv->spn);
self->spn = priv->spn = g_strdup(spn);
fake_sailfish_watch_signal_queue(self,
WATCH_SIGNAL_SPN_CHANGED);
}
}
void fake_sailfish_watch_set_ofono_sim(struct sailfish_watch *self,
struct ofono_sim *sim)
{
if (self->sim != sim) {
self->sim = sim;
fake_sailfish_watch_signal_queue(self,
WATCH_SIGNAL_SIM_CHANGED);
if (!sim) {
fake_sailfish_watch_set_ofono_iccid(self, NULL);
fake_sailfish_watch_set_ofono_imsi(self, NULL);
fake_sailfish_watch_set_ofono_spn(self, NULL);
}
}
}
void fake_sailfish_watch_set_ofono_netreg(struct sailfish_watch *self,
struct ofono_netreg *netreg)
{
if (self->netreg != netreg) {
self->netreg = netreg;
fake_sailfish_watch_signal_queue(self,
WATCH_SIGNAL_NETREG_CHANGED);
}
}
static void sailfish_watch_initialize(struct sailfish_watch *self,
const char *path)
{
struct sailfish_watch_priv *priv = self->priv;
self->path = priv->path = g_strdup(path);
}
static void sailfish_watch_destroyed(gpointer key, GObject* obj)
{
GASSERT(sailfish_watch_table);
DBG("%s", (char*)key);
if (sailfish_watch_table) {
GASSERT(g_hash_table_lookup(sailfish_watch_table, key) == obj);
g_hash_table_remove(sailfish_watch_table, key);
if (g_hash_table_size(sailfish_watch_table) == 0) {
g_hash_table_unref(sailfish_watch_table);
sailfish_watch_table = NULL;
}
}
}
struct sailfish_watch *sailfish_watch_new(const char *path)
{
struct sailfish_watch *watch = NULL;
if (path) {
if (sailfish_watch_table) {
watch = sailfish_watch_ref(g_hash_table_lookup(
sailfish_watch_table, path));
}
if (!watch) {
char* key = g_strdup(path);
watch = g_object_new(SAILFISH_WATCH_TYPE, NULL);
sailfish_watch_initialize(watch, path);
if (!sailfish_watch_table) {
/* Create the table on demand */
sailfish_watch_table =
g_hash_table_new_full(g_str_hash,
g_str_equal, g_free, NULL);
}
g_hash_table_replace(sailfish_watch_table, key, watch);
g_object_weak_ref(G_OBJECT(watch),
sailfish_watch_destroyed, key);
DBG_(watch, "created");
}
}
return watch;
}
struct sailfish_watch *sailfish_watch_ref(struct sailfish_watch *self)
{
if (self) {
g_object_ref(SAILFISH_WATCH(self));
}
return self;
}
void sailfish_watch_unref(struct sailfish_watch *self)
{
if (self) {
g_object_unref(SAILFISH_WATCH(self));
}
}
gulong sailfish_watch_add_modem_changed_handler(struct sailfish_watch *self,
sailfish_watch_cb_t cb, void *user_data)
{
return (self && cb) ? g_signal_connect(self,
SIGNAL_MODEM_CHANGED_NAME, G_CALLBACK(cb), user_data) : 0;
}
gulong sailfish_watch_add_online_changed_handler(struct sailfish_watch *self,
sailfish_watch_cb_t cb, void *user_data)
{
return (self && cb) ? g_signal_connect(self,
SIGNAL_ONLINE_CHANGED_NAME, G_CALLBACK(cb), user_data) : 0;
}
gulong sailfish_watch_add_sim_changed_handler(struct sailfish_watch *self,
sailfish_watch_cb_t cb, void *user_data)
{
return (self && cb) ? g_signal_connect(self,
SIGNAL_SIM_CHANGED_NAME, G_CALLBACK(cb), user_data) : 0;
}
gulong sailfish_watch_add_sim_state_changed_handler(struct sailfish_watch *self,
sailfish_watch_cb_t cb, void *user_data)
{
return (self && cb) ? g_signal_connect(self,
SIGNAL_SIM_STATE_CHANGED_NAME, G_CALLBACK(cb), user_data) : 0;
}
gulong sailfish_watch_add_iccid_changed_handler(struct sailfish_watch *self,
sailfish_watch_cb_t cb, void *user_data)
{
return (self && cb) ? g_signal_connect(self,
SIGNAL_ICCID_CHANGED_NAME, G_CALLBACK(cb), user_data) : 0;
}
gulong sailfish_watch_add_imsi_changed_handler(struct sailfish_watch *self,
sailfish_watch_cb_t cb, void *user_data)
{
return (self && cb) ? g_signal_connect(self,
SIGNAL_IMSI_CHANGED_NAME, G_CALLBACK(cb), user_data) : 0;
}
gulong sailfish_watch_add_spn_changed_handler(struct sailfish_watch *self,
sailfish_watch_cb_t cb, void *user_data)
{
return (self && cb) ? g_signal_connect(self,
SIGNAL_SPN_CHANGED_NAME, G_CALLBACK(cb), user_data) : 0;
}
gulong sailfish_watch_add_netreg_changed_handler(struct sailfish_watch *self,
sailfish_watch_cb_t cb, void *user_data)
{
return (self && cb) ? g_signal_connect(self,
SIGNAL_NETREG_CHANGED_NAME, G_CALLBACK(cb), user_data) : 0;
}
void sailfish_watch_remove_handler(struct sailfish_watch *self, gulong id)
{
if (self && id) {
g_signal_handler_disconnect(self, id);
}
}
void sailfish_watch_remove_handlers(struct sailfish_watch *self, gulong *ids,
int count)
{
gutil_disconnect_handlers(self, ids, count);
}
static void sailfish_watch_init(struct sailfish_watch *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, SAILFISH_WATCH_TYPE,
struct sailfish_watch_priv);
}
static void sailfish_watch_finalize(GObject *object)
{
struct sailfish_watch *self = SAILFISH_WATCH(object);
struct sailfish_watch_priv *priv = self->priv;
g_free(priv->path);
g_free(priv->iccid);
g_free(priv->imsi);
g_free(priv->spn);
G_OBJECT_CLASS(sailfish_watch_parent_class)->finalize(object);
}
static void sailfish_watch_class_init(SailfishWatchClass *klass)
{
G_OBJECT_CLASS(klass)->finalize = sailfish_watch_finalize;
g_type_class_add_private(klass, sizeof(struct sailfish_watch_priv));
NEW_SIGNAL(klass, MODEM);
NEW_SIGNAL(klass, ONLINE);
NEW_SIGNAL(klass, SIM);
NEW_SIGNAL(klass, SIM_STATE);
NEW_SIGNAL(klass, ICCID);
NEW_SIGNAL(klass, IMSI);
NEW_SIGNAL(klass, SPN);
NEW_SIGNAL(klass, NETREG);
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -0,0 +1,55 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 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
* 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.
*/
#ifndef SAILFISH_FAKE_WATCH_H
#define SAILFISH_FAKE_WATCH_H
#include "sailfish_watch.h"
enum sailfish_watch_signal {
WATCH_SIGNAL_MODEM_CHANGED,
WATCH_SIGNAL_ONLINE_CHANGED,
WATCH_SIGNAL_SIM_CHANGED,
WATCH_SIGNAL_SIM_STATE_CHANGED,
WATCH_SIGNAL_ICCID_CHANGED,
WATCH_SIGNAL_IMSI_CHANGED,
WATCH_SIGNAL_SPN_CHANGED,
WATCH_SIGNAL_NETREG_CHANGED,
WATCH_SIGNAL_COUNT
};
void fake_sailfish_watch_signal_queue(struct sailfish_watch *watch,
enum sailfish_watch_signal id);
void fake_sailfish_watch_emit_queued_signals(struct sailfish_watch *watch);
void fake_sailfish_watch_set_ofono_sim(struct sailfish_watch *watch,
struct ofono_sim *sim);
void fake_sailfish_watch_set_ofono_iccid(struct sailfish_watch *watch,
const char *iccid);
void fake_sailfish_watch_set_ofono_imsi(struct sailfish_watch *watch,
const char *imsi);
void fake_sailfish_watch_set_ofono_spn(struct sailfish_watch *watch,
const char *spn);
void fake_sailfish_watch_set_ofono_netreg(struct sailfish_watch *watch,
struct ofono_netreg *netreg);
#endif /* FAKE_SAILFISH_WATCH_H */
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -0,0 +1,280 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2016 Canonical 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
* 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 <stdio.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <ofono/types.h>
#include <gril.h>
#include "rilmodem-test-engine.h"
#define MAX_REQUEST_SIZE 4096
#define RIL_SERVER_SOCK_PATH "/tmp/unittestril"
static GMainLoop *mainloop;
struct engine_data {
int server_sk;
int connected_sk;
guint connection_watch;
rilmodem_test_engine_cb_t connect_func;
GIOChannel *server_io;
char *sock_name;
struct rilmodem_test_data rtd;
int step_i;
void *user_data;
};
static void send_parcel(struct engine_data *ed)
{
GIOStatus status;
gsize wbytes;
const struct rilmodem_test_step *step = &ed->rtd.steps[ed->step_i];
status = g_io_channel_write_chars(ed->server_io,
step->parcel_data,
step->parcel_size,
&wbytes, NULL);
g_assert(wbytes == step->parcel_size);
g_assert(status == G_IO_STATUS_NORMAL);
status = g_io_channel_flush(ed->server_io, NULL);
g_assert(status == G_IO_STATUS_NORMAL);
rilmodem_test_engine_next_step(ed);
}
static gboolean on_rx_data(GIOChannel *chan, GIOCondition cond, gpointer data)
{
struct engine_data *ed = data;
GIOStatus status;
gsize rbytes;
gchar *buf;
const struct rilmodem_test_step *step;
/* We have closed the socket */
if (cond == G_IO_NVAL)
return FALSE;
buf = g_malloc0(MAX_REQUEST_SIZE);
status = g_io_channel_read_chars(ed->server_io, buf, MAX_REQUEST_SIZE,
&rbytes, NULL);
g_assert(status == G_IO_STATUS_NORMAL);
/* Check this is the expected step */
step = &ed->rtd.steps[ed->step_i];
g_assert(step->type == TST_EVENT_RECEIVE);
g_assert(rbytes == step->parcel_size);
/* validate received parcel */
g_assert(!memcmp(buf, step->parcel_data, rbytes));
rilmodem_test_engine_next_step(ed);
return TRUE;
}
static gboolean on_socket_connected(GIOChannel *chan, GIOCondition cond,
gpointer data)
{
struct engine_data *ed = data;
struct sockaddr saddr;
unsigned int len = sizeof(saddr);
GIOStatus status;
g_assert(cond == G_IO_IN);
ed->connected_sk = accept(ed->server_sk, &saddr, &len);
g_assert(ed->connected_sk != -1);
ed->server_io = g_io_channel_unix_new(ed->connected_sk);
g_assert(ed->server_io != NULL);
status = g_io_channel_set_encoding(ed->server_io, NULL, NULL);
g_assert(status == G_IO_STATUS_NORMAL);
g_io_channel_set_buffered(ed->server_io, FALSE);
g_io_channel_set_close_on_unref(ed->server_io, TRUE);
if (ed->connect_func)
ed->connect_func(ed->user_data);
ed->connection_watch =
g_io_add_watch_full(ed->server_io, G_PRIORITY_DEFAULT,
G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
on_rx_data, ed, NULL);
g_io_channel_unref(ed->server_io);
return FALSE;
}
void rilmodem_test_engine_remove(struct engine_data *ed)
{
if (ed->connection_watch)
g_source_remove(ed->connection_watch);
g_assert(ed->server_sk);
close(ed->server_sk);
remove(ed->sock_name);
g_free(ed->sock_name);
g_free(ed);
}
struct engine_data *rilmodem_test_engine_create(
rilmodem_test_engine_cb_t connect,
const struct rilmodem_test_data *test_data,
void *data)
{
GIOChannel *io;
struct sockaddr_un addr;
int retval;
struct engine_data *ed;
ed = g_new0(struct engine_data, 1);
ed->connect_func = connect;
ed->user_data = data;
ed->rtd = *test_data;
ed->server_sk = socket(AF_UNIX, SOCK_STREAM, 0);
g_assert(ed->server_sk);
ed->sock_name =
g_strdup_printf(RIL_SERVER_SOCK_PATH"%u", (unsigned) getpid());
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, ed->sock_name, sizeof(addr.sun_path) - 1);
/* Unlink any existing socket for this session */
unlink(addr.sun_path);
retval = bind(ed->server_sk, (struct sockaddr *) &addr, sizeof(addr));
g_assert(retval >= 0);
retval = listen(ed->server_sk, 0);
g_assert(retval >= 0);
io = g_io_channel_unix_new(ed->server_sk);
g_assert(io != NULL);
g_io_channel_set_close_on_unref(io, TRUE);
g_io_add_watch_full(io, G_PRIORITY_DEFAULT,
G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
on_socket_connected, ed, NULL);
g_io_channel_unref(io);
return ed;
}
void rilmodem_test_engine_write_socket(struct engine_data *ed,
const unsigned char *buf,
const size_t buf_len)
{
GIOStatus status;
gsize wbytes;
status = g_io_channel_write_chars(ed->server_io,
(const char *) buf,
buf_len,
&wbytes, NULL);
g_assert(status == G_IO_STATUS_NORMAL);
status = g_io_channel_flush(ed->server_io, NULL);
g_assert(status == G_IO_STATUS_NORMAL);
}
const char *rilmodem_test_engine_get_socket_name(struct engine_data *ed)
{
return ed->sock_name;
}
static gboolean action_call(gpointer data)
{
struct engine_data *ed = data;
const struct rilmodem_test_step *step;
step = &ed->rtd.steps[ed->step_i];
step->call_action(ed->user_data);
return FALSE;
}
void rilmodem_test_engine_next_step(struct engine_data *ed)
{
const struct rilmodem_test_step *step;
ed->step_i++;
if (ed->step_i >= ed->rtd.num_steps) {
/* Finish the test */
g_main_loop_quit(mainloop);
return;
}
step = &ed->rtd.steps[ed->step_i];
/* If next step is an action, execute it */
switch (step->type) {
case TST_ACTION_SEND:
send_parcel(ed);
break;
case TST_ACTION_CALL:
g_idle_add(action_call, ed);
break;
case TST_EVENT_RECEIVE:
case TST_EVENT_CALL:
break;
};
}
const struct rilmodem_test_step *rilmodem_test_engine_get_current_step(
struct engine_data *ed)
{
const struct rilmodem_test_step *step = &ed->rtd.steps[ed->step_i];
return step;
}
void rilmodem_test_engine_start(struct engine_data *ed)
{
mainloop = g_main_loop_new(NULL, FALSE);
g_main_loop_run(mainloop);
g_main_loop_unref(mainloop);
}

View File

@@ -0,0 +1,74 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2016 Canonical 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
* 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
*
*/
struct engine_data;
enum test_step_type {
TST_ACTION_SEND,
TST_ACTION_CALL,
TST_EVENT_RECEIVE,
TST_EVENT_CALL,
};
typedef void (*rilmodem_test_engine_cb_t)(void *data);
struct rilmodem_test_step {
enum test_step_type type;
union {
/* For TST_ACTION_CALL */
rilmodem_test_engine_cb_t call_action;
/* For TST_ACTION_SEND or TST_EVENT_RECEIVE */
struct {
const char *parcel_data;
const size_t parcel_size;
};
/* For TST_EVENT_CALL */
struct {
void (*call_func)(void);
void (*check_func)(void);
};
};
};
struct rilmodem_test_data {
const struct rilmodem_test_step *steps;
int num_steps;
};
void rilmodem_test_engine_remove(struct engine_data *ed);
struct engine_data *rilmodem_test_engine_create(
rilmodem_test_engine_cb_t connect,
const struct rilmodem_test_data *test_data,
void *data);
void rilmodem_test_engine_write_socket(struct engine_data *ed,
const unsigned char *buf,
const size_t buf_len);
const char *rilmodem_test_engine_get_socket_name(struct engine_data *ed);
void rilmodem_test_engine_next_step(struct engine_data *ed);
const struct rilmodem_test_step *rilmodem_test_engine_get_current_step(
struct engine_data *ed);
void rilmodem_test_engine_start(struct engine_data *ed);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,750 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2016 Canonical 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
* 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 <assert.h>
#include <errno.h>
#include <glib.h>
#include <stdio.h>
#include <netinet/in.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <ofono/modem.h>
#include <ofono/types.h>
#include <ofono/gprs.h>
#include <gril.h>
#include <drivers/rilmodem/rilutil.h>
#include "common.h"
#include "ril_constants.h"
#include "rilmodem-test-engine.h"
static const struct ofono_gprs_driver *gprs_drv;
/* Declarations && Re-implementations of core functions. */
void ril_gprs_exit(void);
void ril_gprs_init(void);
struct ofono_modem;
struct ofono_gprs {
void *driver_data;
GRil *ril;
struct ofono_modem *modem;
struct engine_data *engined;
};
struct ofono_modem {
struct ofono_gprs *gprs;
};
int ofono_gprs_driver_register(const struct ofono_gprs_driver *d)
{
if (gprs_drv == NULL)
gprs_drv = d;
return 0;
}
void ofono_gprs_driver_unregister(const struct ofono_gprs_driver *d)
{
gprs_drv = NULL;
}
void ofono_gprs_register(struct ofono_gprs *gprs)
{
const struct rilmodem_test_step *step;
step = rilmodem_test_engine_get_current_step(gprs->engined);
g_assert(step->type == TST_EVENT_CALL);
g_assert(step->call_func == (void (*)(void)) ofono_gprs_register);
rilmodem_test_engine_next_step(gprs->engined);
}
void ofono_gprs_set_data(struct ofono_gprs *gprs, void *data)
{
gprs->driver_data = data;
}
void *ofono_gprs_get_data(struct ofono_gprs *gprs)
{
return gprs->driver_data;
}
void ofono_gprs_status_notify(struct ofono_gprs *gprs, int status)
{
const struct rilmodem_test_step *step;
step = rilmodem_test_engine_get_current_step(gprs->engined);
g_assert(step->type == TST_EVENT_CALL);
g_assert(step->call_func == (void (*)(void)) ofono_gprs_status_notify);
if (step->check_func != NULL)
((void (*)(struct ofono_gprs *, int)) step->check_func)(
gprs, status);
rilmodem_test_engine_next_step(gprs->engined);
}
void ofono_gprs_detached_notify(struct ofono_gprs *gprs)
{
const struct rilmodem_test_step *step;
step = rilmodem_test_engine_get_current_step(gprs->engined);
g_assert(step->type == TST_EVENT_CALL);
g_assert(step->call_func ==
(void (*)(void)) ofono_gprs_detached_notify);
rilmodem_test_engine_next_step(gprs->engined);
}
void ofono_gprs_bearer_notify(struct ofono_gprs *gprs, int bearer)
{
const struct rilmodem_test_step *step;
step = rilmodem_test_engine_get_current_step(gprs->engined);
g_assert(step->type == TST_EVENT_CALL);
g_assert(step->call_func == (void (*)(void)) ofono_gprs_bearer_notify);
if (step->check_func != NULL)
((void (*)(struct ofono_gprs *, int)) step->check_func)(
gprs, bearer);
rilmodem_test_engine_next_step(gprs->engined);
}
void ofono_gprs_set_cid_range(struct ofono_gprs *gprs,
unsigned int min, unsigned int max)
{
const struct rilmodem_test_step *step;
step = rilmodem_test_engine_get_current_step(gprs->engined);
g_assert(step->type == TST_EVENT_CALL);
g_assert(step->call_func == (void (*)(void)) ofono_gprs_set_cid_range);
if (step->check_func != NULL)
((void (*)(struct ofono_gprs *, unsigned int, unsigned int))
step->check_func)(gprs, min, max);
rilmodem_test_engine_next_step(gprs->engined);
}
struct ofono_modem *ofono_gprs_get_modem(struct ofono_gprs *gprs)
{
return gprs->modem;
}
int ofono_modem_set_integer(struct ofono_modem *modem,
const char *key, int value)
{
const struct rilmodem_test_step *step;
step = rilmodem_test_engine_get_current_step(modem->gprs->engined);
g_assert(step->type == TST_EVENT_CALL);
g_assert(step->call_func == (void (*)(void)) ofono_modem_set_integer);
if (step->check_func != NULL)
((void (*)(struct ofono_modem *, const char *, int))
step->check_func)(modem, key, value);
rilmodem_test_engine_next_step(modem->gprs->engined);
return 0;
}
void ofono_gprs_remove(struct ofono_gprs *gprs)
{
const struct rilmodem_test_step *step;
step = rilmodem_test_engine_get_current_step(gprs->engined);
g_assert(step->type == TST_EVENT_CALL);
g_assert(step->call_func == (void (*)(void)) ofono_gprs_remove);
rilmodem_test_engine_next_step(gprs->engined);
}
/*
* As all our architectures are little-endian except for
* PowerPC, and the Binder wire-format differs slightly
* depending on endian-ness, the following guards against test
* failures when run on PowerPC.
*/
#if BYTE_ORDER == LITTLE_ENDIAN
/* REQUEST_DATA_CALL_LIST, seq 1 */
static const char parcel_req_data_call_list_1_1[] = {
0x00, 0x00, 0x00, 0x08, 0x39, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00
};
/*
* --- TEST 1 ---
* Step 1: Driver sends REQUEST_DATA_CALL_LIST
*/
static const struct rilmodem_test_step steps_test_1[] = {
{
.type = TST_EVENT_RECEIVE,
.parcel_data = parcel_req_data_call_list_1_1,
.parcel_size = sizeof(parcel_req_data_call_list_1_1)
}
};
struct rilmodem_test_data test_1 = {
.steps = steps_test_1,
.num_steps = G_N_ELEMENTS(steps_test_1)
};
/* REQUEST_DATA_CALL_LIST, seq 1 */
static const char parcel_req_data_call_list_2_1[] = {
0x00, 0x00, 0x00, 0x08, 0x39, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00
};
/* Response, no errors */
static const char parcel_rsp_data_call_list_2_2[] = {
0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
/* REQUEST_DATA_REGISTRATION_STATE, seq 2 */
static const char parcel_req_data_registration_state_2_3[] = {
0x00, 0x00, 0x00, 0x08, 0x15, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00
};
/* Responso, no error, {unregistered,0xb08,0x10e1,GPRS,(null),4} */
static const char parcel_rsp_data_registration_state_2_4[] = {
0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x30, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x30, 0x00, 0x62, 0x00,
0x30, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x31, 0x00, 0x30, 0x00,
0x65, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x31, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00,
0x34, 0x00, 0x00, 0x00
};
static void set_cid_range_check_2_5(struct ofono_gprs *gprs,
unsigned int min, unsigned int max)
{
g_assert(min == 1);
g_assert(max == 2);
}
static void gprs_cb_2_8(const struct ofono_error *error, void *data)
{
struct ofono_gprs *gprs = data;
g_assert(error->type == OFONO_ERROR_TYPE_NO_ERROR);
rilmodem_test_engine_next_step(gprs->engined);
}
static void call_set_attached_2_7(gpointer data)
{
struct ofono_gprs *gprs = data;
gprs_drv->set_attached(gprs, 0, gprs_cb_2_8, gprs);
rilmodem_test_engine_next_step(gprs->engined);
}
/*
* --- TEST 2 ---
* Step 1: Driver sends REQUEST_DATA_CALL_LIST
* Step 2: Harness answers with empty data call list
* Step 3: Driver sends REQUEST_DATA_REGISTRATION_STATE
* Step 4: Harness answers with status unregistered
* Step 5: Driver calls ofono_gprs_set_cid_range
* Step 6: Driver calls ofono_gprs_register
* Step 7: Harness calls drv->set_attached(false)
* Step 8: Driver calls the callback specified in step 7
*/
static const struct rilmodem_test_step steps_test_2[] = {
{
.type = TST_EVENT_RECEIVE,
.parcel_data = parcel_req_data_call_list_2_1,
.parcel_size = sizeof(parcel_req_data_call_list_2_1)
},
{
.type = TST_ACTION_SEND,
.parcel_data = parcel_rsp_data_call_list_2_2,
.parcel_size = sizeof(parcel_rsp_data_call_list_2_2)
},
{
.type = TST_EVENT_RECEIVE,
.parcel_data = parcel_req_data_registration_state_2_3,
.parcel_size = sizeof(parcel_req_data_registration_state_2_3)
},
{
.type = TST_ACTION_SEND,
.parcel_data = parcel_rsp_data_registration_state_2_4,
.parcel_size = sizeof(parcel_rsp_data_registration_state_2_4)
},
{
.type = TST_EVENT_CALL,
.call_func = (void (*)(void)) ofono_gprs_set_cid_range,
.check_func = (void (*)(void)) set_cid_range_check_2_5
},
{
.type = TST_EVENT_CALL,
.call_func = (void (*)(void)) ofono_gprs_register,
.check_func = NULL
},
{
.type = TST_ACTION_CALL,
.call_action = call_set_attached_2_7,
},
{
.type = TST_EVENT_CALL,
.call_func = (void (*)(void)) gprs_cb_2_8,
.check_func = NULL
},
};
struct rilmodem_test_data test_2 = {
.steps = steps_test_2,
.num_steps = G_N_ELEMENTS(steps_test_2)
};
static void gprs_cb_3_8(const struct ofono_error *error, void *data)
{
struct ofono_gprs *gprs = data;
g_assert(error->type == OFONO_ERROR_TYPE_NO_ERROR);
rilmodem_test_engine_next_step(gprs->engined);
}
static void call_set_attached_3_7(gpointer data)
{
struct ofono_gprs *gprs = data;
gprs_drv->set_attached(gprs, 1, gprs_cb_3_8, gprs);
rilmodem_test_engine_next_step(gprs->engined);
}
/*
* --- TEST 3 ---
* Steps 1-6: Same as in test 2
* Step 7: Harness calls drv->set_attached(true)
* Step 8: Driver calls the callback specified in step 7
*/
static const struct rilmodem_test_step steps_test_3[] = {
{
.type = TST_EVENT_RECEIVE,
.parcel_data = parcel_req_data_call_list_2_1,
.parcel_size = sizeof(parcel_req_data_call_list_2_1)
},
{
.type = TST_ACTION_SEND,
.parcel_data = parcel_rsp_data_call_list_2_2,
.parcel_size = sizeof(parcel_rsp_data_call_list_2_2)
},
{
.type = TST_EVENT_RECEIVE,
.parcel_data = parcel_req_data_registration_state_2_3,
.parcel_size = sizeof(parcel_req_data_registration_state_2_3)
},
{
.type = TST_ACTION_SEND,
.parcel_data = parcel_rsp_data_registration_state_2_4,
.parcel_size = sizeof(parcel_rsp_data_registration_state_2_4)
},
{
.type = TST_EVENT_CALL,
.call_func = (void (*)(void)) ofono_gprs_set_cid_range,
.check_func = (void (*)(void)) set_cid_range_check_2_5
},
{
.type = TST_EVENT_CALL,
.call_func = (void (*)(void)) ofono_gprs_register,
.check_func = NULL
},
{
.type = TST_ACTION_CALL,
.call_action = call_set_attached_3_7,
},
{
.type = TST_EVENT_CALL,
.call_func = (void (*)(void)) gprs_cb_3_8,
.check_func = NULL
},
};
struct rilmodem_test_data test_3 = {
.steps = steps_test_3,
.num_steps = G_N_ELEMENTS(steps_test_3)
};
/* REQUEST_DATA_REGISTRATION_STATE, seq 3 */
static const char parcel_req_registration_state_4_8[] = {
0x00, 0x00, 0x00, 0x08, 0x15, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00
};
/* Response, no error, {registered,0xb08,0x10e1,GPRS,(null),4} */
static const char parcel_rsp_registration_state_4_9[] = {
0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x30, 0x00, 0x62, 0x00,
0x30, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x31, 0x00, 0x30, 0x00,
0x65, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x31, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00,
0x34, 0x00, 0x00, 0x00
};
static void reg_state_cb_4_12(const struct ofono_error *error,
int status, void *data)
{
struct ofono_gprs *gprs = data;
const struct rilmodem_test_step *step;
step = rilmodem_test_engine_get_current_step(gprs->engined);
g_assert(step->type == TST_EVENT_CALL);
g_assert(step->call_func == (void (*)(void)) reg_state_cb_4_12);
g_assert(error->type == OFONO_ERROR_TYPE_NO_ERROR);
/*
* Driver returns unregistered even though network state is attached
* because we did not set attach to true in this test case.
*/
g_assert(status == NETWORK_REGISTRATION_STATUS_NOT_REGISTERED);
rilmodem_test_engine_next_step(gprs->engined);
}
static void call_registration_status_4_7(gpointer data)
{
struct ofono_gprs *gprs = data;
gprs_drv->attached_status(gprs, reg_state_cb_4_12, gprs);
rilmodem_test_engine_next_step(gprs->engined);
}
static void set_integer_check_4_10(struct ofono_modem *modem,
const char *key, int value)
{
g_assert_cmpstr(key, ==, "RilDataRadioTechnology");
g_assert(value == RADIO_TECH_GPRS);
}
static void gprs_bearer_check_4_11(struct ofono_gprs *gprs, int bearer)
{
g_assert(bearer == PACKET_BEARER_GPRS);
}
/*
* --- TEST 4 ---
* Steps 1-6: Same as in test 2
* Step 7: Harness calls drv->registration_status
* Step 8: Driver sends REQUEST_DATA_REGISTRATION_STATE
* Step 9: Harness answers saying status is registered
* Step 10: Driver calls ofono_modem_set_integer
* Step 11: Driver calls ofono_gprs_bearer_notify(PACKET_BEARER_GPRS)
* Step 12: Driver calls the callback specified in step 7
*/
static const struct rilmodem_test_step steps_test_4[] = {
{
.type = TST_EVENT_RECEIVE,
.parcel_data = parcel_req_data_call_list_2_1,
.parcel_size = sizeof(parcel_req_data_call_list_2_1)
},
{
.type = TST_ACTION_SEND,
.parcel_data = parcel_rsp_data_call_list_2_2,
.parcel_size = sizeof(parcel_rsp_data_call_list_2_2)
},
{
.type = TST_EVENT_RECEIVE,
.parcel_data = parcel_req_data_registration_state_2_3,
.parcel_size = sizeof(parcel_req_data_registration_state_2_3)
},
{
.type = TST_ACTION_SEND,
.parcel_data = parcel_rsp_data_registration_state_2_4,
.parcel_size = sizeof(parcel_rsp_data_registration_state_2_4)
},
{
.type = TST_EVENT_CALL,
.call_func = (void (*)(void)) ofono_gprs_set_cid_range,
.check_func = (void (*)(void)) set_cid_range_check_2_5
},
{
.type = TST_EVENT_CALL,
.call_func = (void (*)(void)) ofono_gprs_register,
.check_func = NULL
},
{
.type = TST_ACTION_CALL,
.call_action = call_registration_status_4_7,
},
{
.type = TST_EVENT_RECEIVE,
.parcel_data = parcel_req_registration_state_4_8,
.parcel_size = sizeof(parcel_req_registration_state_4_8)
},
{
.type = TST_ACTION_SEND,
.parcel_data = parcel_rsp_registration_state_4_9,
.parcel_size = sizeof(parcel_rsp_registration_state_4_9)
},
{
.type = TST_EVENT_CALL,
.call_func = (void (*)(void)) ofono_modem_set_integer,
.check_func = (void (*)(void)) set_integer_check_4_10
},
{
.type = TST_EVENT_CALL,
.call_func = (void (*)(void)) ofono_gprs_bearer_notify,
.check_func = (void (*)(void)) gprs_bearer_check_4_11
},
{
.type = TST_EVENT_CALL,
.call_func = (void (*)(void)) reg_state_cb_4_12,
.check_func = NULL
},
};
struct rilmodem_test_data test_4 = {
.steps = steps_test_4,
.num_steps = G_N_ELEMENTS(steps_test_4)
};
/* UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED */
static const char parcel_ev_network_state_changed_5_9[] = {
0x00, 0x00, 0x00, 0x08, 0x01, 0x00, 0x00, 0x00, 0xEA, 0x03, 0x00, 0x00
};
/* REQUEST_DATA_REGISTRATION_STATE, seq 3 */
static const char parcel_req_registration_state_5_10[] = {
0x00, 0x00, 0x00, 0x08, 0x15, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00
};
/* Response, no error, {registered,0xb08,0x10e1,GPRS,(null),4} */
static const char parcel_rsp_registration_state_5_11[] = {
0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x31, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x30, 0x00, 0x62, 0x00,
0x30, 0x00, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x30, 0x00, 0x31, 0x00, 0x30, 0x00,
0x65, 0x00, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
0x31, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x01, 0x00, 0x00, 0x00,
0x34, 0x00, 0x00, 0x00
};
static void gprs_status_check_5_12(struct ofono_gprs *gprs, int status)
{
g_assert(status == NETWORK_REGISTRATION_STATUS_REGISTERED);
}
static void set_integer_check_5_13(struct ofono_modem *modem,
const char *key, int value)
{
g_assert_cmpstr(key, ==, "RilDataRadioTechnology");
g_assert(value == RADIO_TECH_GPRS);
}
static void gprs_bearer_check_5_14(struct ofono_gprs *gprs, int bearer)
{
g_assert(bearer == PACKET_BEARER_GPRS);
}
/*
* --- TEST 5 ---
* Steps 1-8: Same as test 3
* Step 9: Harness sends UNSOL_RESPONSE_VOICE_NETWORK_STATE_CHANGED
* Step 10: Driver sends REQUEST_DATA_REGISTRATION_STATE
* Step 11: Harness answers saying status is registered
* Step 12: Driver calls ofono_gprs_status_notify(REGISTERED)
* Step 13: Driver calls ofono_modem_set_integer
* Step 14: Driver calls ofono_gprs_bearer_notify(PACKET_BEARER_GPRS)
*/
static const struct rilmodem_test_step steps_test_5[] = {
{
.type = TST_EVENT_RECEIVE,
.parcel_data = parcel_req_data_call_list_2_1,
.parcel_size = sizeof(parcel_req_data_call_list_2_1)
},
{
.type = TST_ACTION_SEND,
.parcel_data = parcel_rsp_data_call_list_2_2,
.parcel_size = sizeof(parcel_rsp_data_call_list_2_2)
},
{
.type = TST_EVENT_RECEIVE,
.parcel_data = parcel_req_data_registration_state_2_3,
.parcel_size = sizeof(parcel_req_data_registration_state_2_3)
},
{
.type = TST_ACTION_SEND,
.parcel_data = parcel_rsp_data_registration_state_2_4,
.parcel_size = sizeof(parcel_rsp_data_registration_state_2_4)
},
{
.type = TST_EVENT_CALL,
.call_func = (void (*)(void)) ofono_gprs_set_cid_range,
.check_func = (void (*)(void)) set_cid_range_check_2_5
},
{
.type = TST_EVENT_CALL,
.call_func = (void (*)(void)) ofono_gprs_register,
.check_func = NULL
},
{
.type = TST_ACTION_CALL,
.call_action = call_set_attached_3_7,
},
{
.type = TST_EVENT_CALL,
.call_func = (void (*)(void)) gprs_cb_3_8,
.check_func = NULL
},
{
.type = TST_ACTION_SEND,
.parcel_data = parcel_ev_network_state_changed_5_9,
.parcel_size = sizeof(parcel_ev_network_state_changed_5_9)
},
{
.type = TST_EVENT_RECEIVE,
.parcel_data = parcel_req_registration_state_5_10,
.parcel_size = sizeof(parcel_req_registration_state_5_10)
},
{
.type = TST_ACTION_SEND,
.parcel_data = parcel_rsp_registration_state_5_11,
.parcel_size = sizeof(parcel_rsp_registration_state_5_11)
},
{
.type = TST_EVENT_CALL,
.call_func = (void (*)(void)) ofono_gprs_status_notify,
.check_func = (void (*)(void)) gprs_status_check_5_12
},
{
.type = TST_EVENT_CALL,
.call_func = (void (*)(void)) ofono_modem_set_integer,
.check_func = (void (*)(void)) set_integer_check_5_13
},
{
.type = TST_EVENT_CALL,
.call_func = (void (*)(void)) ofono_gprs_bearer_notify,
.check_func = (void (*)(void)) gprs_bearer_check_5_14
},
};
struct rilmodem_test_data test_5 = {
.steps = steps_test_5,
.num_steps = G_N_ELEMENTS(steps_test_5)
};
static void server_connect_cb(gpointer data)
{
struct ofono_gprs *gprs = data;
int retval;
/*
* This triggers the first event from the gprs atom, which is a request
* to retrieve currently active data calls. Test steps must start from
* there.
*/
retval = gprs_drv->probe(gprs, OFONO_RIL_VENDOR_AOSP, gprs->ril);
g_assert(retval == 0);
}
/*
* This unit test:
* - does some test data setup
* - configures a dummy server socket
* - creates a new gril client instance
* - triggers a connect to the dummy
* server socket
* - starts the test engine
*/
static void test_function(gconstpointer data)
{
const struct rilmodem_test_data *test_data = data;
struct ofono_gprs *gprs;
struct ofono_modem *modem;
ril_gprs_init();
gprs = g_malloc0(sizeof(*gprs));
modem = g_malloc0(sizeof(*modem));
modem->gprs = gprs;
gprs->modem = modem;
gprs->engined = rilmodem_test_engine_create(&server_connect_cb,
test_data, gprs);
gprs->ril = g_ril_new(rilmodem_test_engine_get_socket_name(gprs->engined),
OFONO_RIL_VENDOR_AOSP);
g_assert(gprs->ril != NULL);
/* Perform test */
rilmodem_test_engine_start(gprs->engined);
gprs_drv->remove(gprs);
g_ril_unref(gprs->ril);
g_free(modem);
g_free(gprs);
rilmodem_test_engine_remove(gprs->engined);
ril_gprs_exit();
}
#endif
int main(int argc, char **argv)
{
g_test_init(&argc, &argv, NULL);
/*
* As all our architectures are little-endian except for
* PowerPC, and the Binder wire-format differs slightly
* depending on endian-ness, the following guards against test
* failures when run on PowerPC.
*/
#if BYTE_ORDER == LITTLE_ENDIAN
g_test_add_data_func("/test-rilmodem-gprs/1", &test_1, test_function);
g_test_add_data_func("/test-rilmodem-gprs/2", &test_2, test_function);
g_test_add_data_func("/test-rilmodem-gprs/3", &test_3, test_function);
g_test_add_data_func("/test-rilmodem-gprs/4", &test_4, test_function);
g_test_add_data_func("/test-rilmodem-gprs/5", &test_5, test_function);
#endif
return g_test_run();
}

Some files were not shown because too many files have changed in this diff Show More