Compare commits

...

219 Commits

Author SHA1 Message Date
Slava Monich
e0b4e8694d [ril] Fixed a few compilation warnings 2017-11-14 12:47:49 +02:00
Slava Monich
c8db770c99 [ril] Don't retry REQUEST_GET_CURRENT_CALLS on RADIO_NOT_AVAILABLE
Assume that in this case there's no active voice calls.
2017-11-14 12:41:08 +02:00
Slava Monich
1534143e31 [ril] Don't retry REQUEST_DATA_CALL_LIST on RADIO_NOT_AVAILABLE
Assume that in this case there's no active calls.
2017-11-14 12:38:49 +02:00
Slava Monich
d9ad9caf30 Merge branch 'get_imei' into 'master'
Add legacyImeiQuery configuration option.

See merge request !155
2017-11-14 10:33:56 +00:00
Slava Monich
71de574e87 [ril] Added legacyImeiQuery configuration option. JB#39612
MTK ril doesn't understand RIL_REQUEST_DEVICE_IDENTITY
2017-11-13 22:28:31 +02:00
Slava Monich
11efbd68e6 Bumped spec version to 1.20 2017-11-08 20:48:38 +03:00
Slava Monich
9981f07797 Merge branch 'v1.20' into 'master'
[ofono] Upgrade baseline to 1.20. Fixes JB#40330

See merge request !153
2017-11-08 17:47:17 +00:00
Marcel Holtmann
24733f776e Release 1.20 2017-11-08 18:20:52 +03:00
Alexander Couzens
50ec234239 plugins/gobi: add the qmi type to the debug output of discover_cb() 2017-11-08 18:20:52 +03:00
Alexander Couzens
b4991076c6 udevng/gobi: allow to detect ec20 tty devices
The Quectel EC20 uses the same usb id as some gobi 2000 modules (05c6:9215).
2017-11-08 18:20:52 +03:00
Alexander Couzens
8b02884696 udevng/gobi: use subsystem as first identification
Using kernel 4.10 with systemd 232 on archlinux the detection of
a gobi 2000 doesn't detect the qmi/cdc-wdm interface.

The device is detected as follows:

[devnode interface number label sysattr subsystem]
/dev/cdc-wdm1 255/255/255 00 (null) (null) usbmisc
wwan1 255/255/255 00 (null) (null) net
/dev/ttyUSB5 255/255/255 01 (null) (null) tty
/dev/ttyUSB6 255/255/255 02 (null) (null) tty
/dev/ttyUSB7 255/255/255 03 (null) (null) tty
2017-11-08 18:20:52 +03:00
Alexander Couzens
8e224a21f6 udevng/gobi: improve debug output
Add info->sysattr and info->subsystem to the log message.
2017-11-08 18:20:52 +03:00
Luiz Augusto von Dentz
96e191b2d2 handsfree-audio: Add Acquire implementation
This adds Acquire method which can be used by agents that require
pulling the fd directly instead of waiting a NewConnection.
2017-11-08 18:20:52 +03:00
Luiz Augusto von Dentz
7ae3aad622 doc/handsfree-audio-api: Add Acquire method
This adds Acquire method which can be used by agents that require
pulling the fd directly instead of waiting a NewConnection.

Note: sounds servers like PulseAudio do auto suspend streams when idle
for a certain amount of time and once anything happens it will resume
the stream, though this all happens in the so called IO thread in a
blocking fashion making it impossible to receive the fd via NewConnetion
causing the stream to fail to resume. There are other forms to work
around but this seems to be most convenient as we do want the auto
suspend feature to work properly but letting the stream to fail to
resume may create unexpected artifacts while the NewConnection is
handled in main thread.
2017-11-08 18:20:52 +03:00
Luiz Augusto von Dentz
3d5d88241e hfp_hf_bluez5: Rework code handling device changes
This splits the handling of device changes and modem registration so
they can be uses separately.
2017-11-08 18:20:52 +03:00
Jonas Bonn
31e62567e6 qmimodem: set APN for LTE default bearer
Apparently, an empty APN in an ofono context means that that the context
cannot be activated.  connman definitely interprets it this way.

This patch sets a default name of "automatic" for the default bearer if
no other LTE APN is supplied (which is currently the case as the LTE
atom is not in place yet).  Without this, connman happily ignores the
context, even though it has been activated by ofono.
2017-11-08 18:20:52 +03:00
Jonas Bonn
8c3127ef21 qmi: activate default bearer context for LTE networks
When the modem attaches to an LTE network, a default bearer is
automatically negotiated using the "defalt profile" settings.  The
QMI modem, however, does not given any explicit indication that
the bearer exists; instead, we must assume its existence based on
the network registration state.

This patch extends the GPRS atom to signal the presence of a
default bearer when it detects network connectivity on an LTE
network.
2017-11-08 18:20:51 +03:00
Jonas Bonn
1c1fc4199e qmi: use named status values 2017-11-08 18:20:51 +03:00
Jonas Bonn
bfe2f95c4c qmi: use shared services
Apparently it's not legal to create a QMI service multiple times for
a device.  I've been testing with a Quectel EC21 and here it works fine
to do so, but the general case would require "shared" services across
atoms.

This patch switches the users of the NAS and WDS services over to using
a "shared" service instead of each instatiating their own instance.
2017-11-08 18:20:51 +03:00
Jonas Bonn
7e4d99236b qmi: watch packet status for spontaneous disconnection only
There are three principal ways for a context to become disconnected:

i)  deactivate_primary() is called
ii)  the network registration is lost and the context is cleaned up
via _detach_shutdown() (via release_all_contexts())
iii) the network decides to disconnect the context

We need to watch the packet status in order to detect case iii).  For
case i) and ii), stop_net will be called, the pkt_handle will be
cleared, and subsequent packet status notifications be ignored.

This patch makes it so that the packet status "disconnected" event
is only propagated when the pkt_handle has not been cleared, indicating
an unrequested disconnection.

This should fix a race reported by Christophe Ronco whereby the packet
status disconnect notification is called between activate_primary
(start_net) and it's callback (start_net_cb).
2017-11-08 18:20:51 +03:00
Luiz Augusto von Dentz
0935a227be hfp_hf: Handle ServicesResolved signal
This adds handling for ServicesResolved signal which tells when BlueZ
is done resolving the device services so the code will no longer ignore
devices that got its services resolved after Paired signal.
2017-11-08 18:20:51 +03:00
Luiz Augusto von Dentz
ffdeb3692c hfp_hf: Fix not creating modem during NewConnection
In case the UUIDs are not updated, as they are still being resolved, when
Paired property changes a modem will never be registered.

In order to fix this problem allow modems to be registered directly
during NewConnection.
2017-11-08 18:20:51 +03:00
Vincent Cesson
627904e382 gemalto: Set vendor for sms atom 2017-11-08 18:20:51 +03:00
Vincent Cesson
0ab0677765 atmodem: Fix SMS reception for Gemalto modems
SMS reception is not working for Gemalto modems because of +CMT parsing.
PDU length is the first argument of +CMT URCs in Gemalto modems.

Add a switch case on vendor info to handle Gemalto case.
Also handle acknowledgment, +CNMA takes only one parameter.

CMT parsing is moved from at_parse_cmt() to at_cmt_notify(). This
function is modified to match the style of at_cmgr_notify() and it
includes a switch case for CINTERION modems.
2017-11-08 18:20:51 +03:00
Jonas Bonn
fe6af108ca Add support for Quectel EC21/25 modems
This follows the same pattern as the Telit QMI modems, routing the
setup through a QMI driver.  I think this can be cleaned up somehow,
but for now this at least provides support for these two modem models
so that others can aid in testing.
2017-11-08 18:20:51 +03:00
Vincent Cesson
650ff3642f gemalto: Use sim_state_query for sim detection
Populate gemalto_data structure.
Add sim_state callbacks.
Fix enable/disable return value.
2017-11-08 18:20:51 +03:00
Jonas Bonn
41d310aa61 qmi: move rat_to_tech() into own module
We want to use this function from multiple atoms so this patch moves
it out to its own module for NAS-related helper functions.
2017-11-08 18:20:51 +03:00
Jonas Bonn
27adf83a4b ofono: add missing header inclusions
This is a "leaf" header and doesn't even have header guards, but
it still seems natural that the header should pull in its own declarations
rather than relying on the including source file to ensure that they
are included.
2017-11-08 18:20:51 +03:00
Jonas Bonn
55d227ba46 qmi: add missing header inclusion 2017-11-08 18:20:51 +03:00
Jonas Bonn
dcc1d366f0 qim: use named status value 2017-11-08 18:20:51 +03:00
Jonas Bonn
83cf94824d qmi: implement detach_shutdown method
The detach_shutdown method is invoked to unconditionally release
an active context.  For QMI, this is equivalent to a call to
deactivate_primary.

This patch makes the callback to deactivate_primary optional and
implements detach_shutdown to simply call it.  When there is no
callback, the stop_net callback notifies ofono about the context
release via an asynchronous ofono_gprs_context_deactivated() call.
2017-11-08 18:20:51 +03:00
Jonas Bonn
3a0c598805 qmi: free cb_data on error
...and move allocation of structure up to variable declarations to
match the pattern used elsewhere in the code.
2017-11-08 18:20:51 +03:00
Jonas Bonn
b098314251 qmi: read_settings needs to call start network
For LTE networks, a default bearer is automatically activated when
the modem registers to the network.  QMI modems, however, do not
automatically enable the network interface just because the bearer
exists; a call to "start network" needs to be made in order to
get the packet handle before get_settings will return any data and
the network interface can be configured.

This patch makes read_settings call "start network" in order to
enable the interface for the default bearer.  No new bearer will
be created with this call and the settings for the bearer will come
from the default profile, irregardless of what parameters are passed
to the "start network" method.
2017-11-08 18:20:51 +03:00
Jonas Bonn
4ae6c6c0b1 gprs: set driver_attached when activating automatic contexts
The ofono_gprs_cid_activated attachment machinery cannot go through
ofono_gprs_status_notify for getting the attached property set because
that would result in the automatic contexts that were just set up
being released.  As such, it needs to call gprs_set_attached_property
manually.  Doing so, however, means that the driver_attached property
never gets set, resulting in all contexts being released when the
network transitions between registered states (roaming/non-roaming).
2017-11-08 18:20:51 +03:00
Jonas Bonn
d5f0f3b32d gprs: _cid_activated is an 'attaching' state
ofono_gprs_status_notify is an asynchronous notification that messes
with the 'attached' state of the GPRS atom.  This method is normally
prevented from running while an attach is in progress because the
attachment machinery wants to finish up and make it's own determination
of attach state.

When automatic context activation is relevant, as for LTE networks,
the ofono_gprs_cid_activated machinery replaces the usual set_attach
machinery for attaching to the network.  The cid_activated variant,
however, does not guard against simulatenous invocations of
ofono_gprs_status_notify.  This causes a race whereby status_notify
sets the state to 'attached' before the context is fully constructed
and set to active.  If the connection manager sees the 'attached'
state before there are any 'active' contexts, it may decide to
activate a context manually which is not the correct behaviour for
this type of network.

This patch makes the *_cid_activated machinery an 'attaching' state,
introducing the same guards that set_attached has to prevent
ofono_gprs_status_notify from running concurrently.
2017-11-08 18:20:51 +03:00
Jonas Bonn
0209e9847b qmi: fix typo 2017-11-08 18:20:51 +03:00
Jonas Bonn
a499ac07ca qmi: duplicate callback data correctly 2017-11-08 18:20:51 +03:00
Vincent Cesson
04342bbe69 gemalto: Change offline mode, keep USIM available
Gemalto has two airplane mode:
CFUN=0 disables USIM
CFUN=4 keeps USIM connected
2017-11-08 18:20:51 +03:00
Vincent Cesson
d0d3e4f2f1 gemalto: Clean post init functions
Remove inappropriate interfaces.
Move GPRS init from post_online to post_sim.
2017-11-08 18:20:51 +03:00
Denis Kenzior
edcbc5c7e3 AUTHORS: Mention Jonas' contributions 2017-11-08 18:20:51 +03:00
Denis Kenzior
1df55e3042 qmi: Use destroy callback for context activate 2017-11-08 18:20:51 +03:00
Denis Kenzior
df93fceb4f qmi: Use destroy callback for context deactivate
This ensures that cbd will be freed even if the device is hot-unplugged.
Also, this fixes a memory leak on the failure path inside stop_net_cb
2017-11-08 18:20:51 +03:00
Jonas Bonn
d21d1a166f qmi: implement read_settings for automatic contexts
For LTE, a context is created automatically when the modem registers
to the network.  The read_settings function is called for these
automatic contexts to get their configuration.
2017-11-08 18:20:51 +03:00
Jonas Bonn
458f905262 qmi: retrieve GPRS context parameters
The GPRS context needs to be configured with connection parameters when
the bearer has been established.  This was only partially implemented, so
this patch adds additional parameters to those passed to the context.
2017-11-08 18:20:51 +03:00
Jonas Bonn
9f474ba723 qmi: add WDS parameter definition 2017-11-08 18:20:51 +03:00
Jonas Bonn
a3b4421422 qmi: NAS definitions adjustment
Calling the ps_state/cs_state alternatives *ATTACH_STATUS* was confusing
because there is also a status field in the *serving_system structure.
This patch does a minor rename and adds the appropriate definitions for
the status field.
2017-11-08 18:20:50 +03:00
Denis Kenzior
28bc1e37ed qmi: Fix qmi_device_shutdown
qmi_device_shutdown allocated a new orphaned data structure and kicked
off a timeout to wait for the shutdown to complete.  The logic was quite
racy, but the main issue was that the timeouts could not be canceled
when the underlying qmi_device object was destroyed.  This resulted in
crashes.

This patch switches to first-past-the-gate mechanism.  Since only the
modem driver should be issuing a qmi_device_shutdown call, this should
not be a limitation.  The shutdown source is then tracked on the
qmi_device object itself and is canceled when the qmi_device object is
freed.

As an added bonus, the shutdown_destroy callback should now actually
function.  Before it was simply never called.
2017-11-08 18:20:50 +03:00
Denis Kenzior
c0b96a4319 qmi: Optimize structure allocations
struct discovery was allocated for every discovery procedure that was
kicked off, which itself allocated a structure.  This patch uses a
class/subclass concept to only allocate a single structure per discovery
procedure.
2017-11-08 18:20:50 +03:00
Jonas Bonn
4296616d00 modem: set_online is valid for AlwaysOnline modems
Calling set_online(TRUE) for an AlwaysOnline modem should succeed; the
modem is, after all, in the requested state when the call returns.
Returning not_implemented is not necessarily wrong, but it's a bit ugly.
2017-11-08 18:20:50 +03:00
Jonas Bonn
3a43f96fe4 qmi: fix bad lookup and double free
This function was never removing discovery instances because it was looking
them up in the wrong list.  This led to some strangeness with the discovery
callbacks being invoked after the "failure" timeout of 5 seconds and
consequent failures with everything getting out of sync.

With this patch we fix the lookup to use the correct queue.  There's also
a double-free in the function that was never being hit before because the
lookups never succeeded; fix that as well.

With this, service discovery and creation work as expected when testing with
an EC21.
2017-11-08 18:20:50 +03:00
Jonas Bonn
b82a1001e2 qmi: add service ID's 2017-11-08 18:20:50 +03:00
Jonas Bonn
84dc7e2016 udevng: remove 'option' driver setup
No driver named 'option' exists so this device can never be instantiated.
2017-11-08 18:20:50 +03:00
Lukasz Nowak
1482728a61 atmodem: telit - re-attach gprs automatically
Telit AT modem firmware (tested with UE910-EUR) generates
+CGREG: 0\r\n\r\n+CGEV: NW DETACH
after a context is de-activated and ppp connection closed.
Then, after a random amount of time (observed from a few seconds
to a few hours), an unsolicited +CGREG: 1 arrives.
Attempt to fix the problem, by sending AT+CGATT=1 once.
This does not re-activate the context, but if a network connection
is still correct, will generate an immediate +CGREG: 1.
2017-11-08 18:20:50 +03:00
Jonas Bonn
428f62041b plugins: remove udev module
This functionality has been moved into the udevng module and was
already disabled in an earlier patch.
2017-11-08 18:20:50 +03:00
Jonas Bonn
4b6ec99973 udevng: get properties from interface
Device properties are generally on the device, on the USB interface
descriptor, or the on the USB device descriptor.
2017-11-08 18:20:50 +03:00
Jonas Bonn
0c01da5378 udevng: hook up legacy devices
...and disable old udev code by shorting it out in it's init() function.

The check_device function is augmented to differentiate between USB
and serial devices:

- if the device sits on a USB bus, the device is handled as before
- if not, an attempt is made to handle the device as a serial device
2017-11-08 18:20:50 +03:00
Jonas Bonn
8004756c3d udevng: match on the hsi subsystem for legacy devices 2017-11-08 18:20:50 +03:00
Jonas Bonn
e01df1a3f1 udevng: add serial device handling functions
This adds, but does not hook up, support for simple serial modems.  These
modems generally have only a single device node so are simpler than the
USB devices which generally have different device nodes for different
functions.  These modems are currently handled by udev.c, but this
functionality will allow to remove that module completely in a later patch.

- A new "device_info" type is created called serial_device_info
- the function add_serial_device sets up a modem_info structure and a
  serial_device_info for the device
- A reference to the device's udev node is saved in the device info
- The device driver is retrieved from the OFONO_DRIVER environment variable
  which needs to be set up by some udev rule
- Setup functions are added for these types of devices: a common function
  setup_serial_modem covers the generic (simple) case, whereas modems
  with special requirements are given their own setup functions to handle
  the special bits
- Modem destroy needs to know the "device_info" type in order to clean
  up properly, so a 'type' value is set on the modem_info structure to
  guide selection of the proper cleanup function
2017-11-08 18:20:50 +03:00
Denis Kenzior
46820a7ba0 qmi: track discovery tasks so clean up is possible
There are various device & service discovery tasks that are initiated
based on a qmi_device object.  qmi_device object does not currently
keep track of these tasks.  Unfortunately the qmi_device object can
go away at any time, and these tasks can become orphaned.

The result of this can lead to crashes.  E.g. a discovery task timeout fires
after the qmi_device object has been destroyed.  Since the object is no
longer valid, any accesses to it will likely result in a SEGFAULT.

This patch attempts to track all discovery tasks on the qmi_device
object itself, so that they can be cleaned up properly.  This patch does
not handle the qmi_device_shutdown functionality.
2017-11-08 18:20:50 +03:00
Lukasz Nowak
0493629a9d unit: rilmodem-test - add missing string.h
Remove warnings of undeclared memcpy, strncpy, etc.
2017-11-08 18:20:50 +03:00
Lukasz Nowak
41b3459a5d udevng: add Telit LE910 V1 support
Tested with LE910-SVG and Verizon.
2017-11-08 18:20:50 +03:00
Jonas Bonn
b27373c8a4 udev: remove extraneous subsystem check
The enumeration construct is already set up with matches for these
subsystems.
2017-11-08 18:20:50 +03:00
Jonas Bonn
b450c8fbe3 udev: simplify wavecom modem registration 2017-11-08 18:20:50 +03:00
Jonas Bonn
1ac24f32e3 udev: simplify ifx modem registration 2017-11-08 18:20:50 +03:00
Jonas Bonn
977fc5bc15 udev: add common modem registration code
Many of these drivers do exactly the same setup when registering
the modem.  Consolidate these setup functions into common code.
2017-11-08 18:20:50 +03:00
Jonas Bonn
787bddf47b udev: simplify add_modem
Adding a modem boils down to finding the device in the hierarchy with
the OFONO_DRIVER property.  The original code special-cased the property
being on the device itself rather than on a parent device.  This patch
combines the two cases.
2017-11-08 18:20:50 +03:00
Jonas Bonn
c32cd532f2 udev: remove unused modem property
The property 'Registered' is not used anywhere.
2017-11-08 18:20:50 +03:00
Jonas Bonn
18f2345124 udev: get udev property via lib function 2017-11-08 18:20:50 +03:00
Jonas Bonn
00b623e8c4 udev: remove extraneous subsystem check
The udev-monitor already guarantees that only devices with these
subsystems will be returned so we don't need to check again.
2017-11-08 18:20:50 +03:00
Jonas Bonn
452108d058 ofono.rules: remove 'change' action
The code doesn't do anything with this action so don't bother setting
extra device properties for it.
2017-11-08 18:20:50 +03:00
Jonas Bonn
6b0712dae4 udevng: simplify logic in check_usb_device
This patch simplifies and cleans up the check_usb_device function a bit
by doing the two following (slightly intertwined) things:

1)  The parent "usb_device" is searched for early in this function and this
device will always have the ID_VENDOR_ID and ID_MODEL_ID properties.
As such, we can get them from this device and thereby be certain that
we _always_ have them available.

2)  The logic of iterating the vendor_list table is cleaned up.  It's
easier to follow and won't be any less efficient.
2017-11-08 18:20:50 +03:00
Jonas Bonn
9a309f499b gobi: query presence of WDA service 2017-11-08 18:20:50 +03:00
Jonas Bonn
a204c993e5 qmimodem: add WDA service string 2017-11-08 18:20:50 +03:00
Jonas Bonn
e881376127 qmimodem: fix typo 2017-11-08 18:20:50 +03:00
Lukasz Nowak
66c98d724c gobi: Do not use low-power modes for some modems
Telit QMI modems have a problem with the low-power operating modes.
After entering and leaving such a state, UIM service does not return.
The sim card is still marked as powered-down. The QMI interface does
not have a way to power it back on.

To avoid this, keep modems with the "AlwaysOnline" flag online
in the disable-modem and offline-modem procedures.
2017-11-08 18:20:49 +03:00
Denis Kenzior
5c38fe6a84 AUTHORS: Mention Lukasz's contributions 2017-11-08 18:20:49 +03:00
Lukasz Nowak
e3bb317504 qmimodem: detect utf-8 string as operator name
Telit QMI modems can return non-utf-8 characters in plmn-desc.
Observed with LE910-SVG and Verizon. When that happens, libdbus
will abort ofono.
If non-utf-8 characters are detected, use mccmnc string.
2017-11-08 18:20:49 +03:00
Lukasz Nowak
713022a7e8 qmimodem: read ss_info at probe time
LTE modems (observed with Telit LE910 V1) can power on
already registered to a network. In that case, the SS_INFO
change notification will never arrive, and the gprs driver
will never be marked as attached.

To avoid this situation, read SS_INFO at probe time, and if
registered, mark the gprs driver as attached.
2017-11-08 18:20:49 +03:00
Denis Kenzior
6b79f32715 qmimodem: Fix warning
This was introduced by the previous patch:
drivers/qmimodem/devinfo.c: In function ‘get_ids_cb’:
drivers/qmimodem/devinfo.c:129:14: error: implicit declaration of function ‘strcmp’ [-Werror=implicit-function-declaration]
  if (!str || strcmp(str, "0") == 0) {
2017-11-08 18:20:49 +03:00
Lukasz Nowak
2386e99ad8 qmimodem: telit LE910 V1 - fix ESN string
Telit QMI modems report "0", rather than a NULL string,
if ESN is not available.
2017-11-08 18:20:49 +03:00
Christophe Ronco
6ca82960c9 huawei: reopen modem channel if it disconnects
On E3372 after a GPRS disconnection, modem AT channel (used by PPP) gets
disconnected (G_IO_HUP and G_IO_ERR are detected in received_data
in gatio.c).
After that all connection attempts fail because we are not able to send
any AT commands on modem channel (it is closed).

With this patch, when this behavior is detected, we close gprs context
driver, reopen AT channel and gprs context driver.
2017-11-08 18:20:49 +03:00
Christophe Ronco
93891578fc provision: add plugin to provision from config file
This plugin allows to provision gprs-context data based on MCC and MNC.
This is useful when provisioning using mbpi fails (either because you
are using a private APN or because there are multiple internet type context
for your operator in mbpi database).
Config file is STORAGEDIR/provisioning.
Config file should look like that:
[operator:MCC1,MNC1]
internet.AccessPointName=apn1
internet.Username=myUsername
internet.Password=myPassword
internet.AuthenticationMethod=pap
internet.Protocol=ipv6
[operator:MCC2,MNC2]
internet.AccessPointName=apn2

The only mandatory parameter for each operator is internet.AccessPointName.

Parameter description:
internet.AccessPointName: APN
internet.Username: Username
internet.Password: Password
internet.AuthenticationMethod: authentication method.
 Possible values are:
  - pap
  - chap
 Default is chap if unset.
internet.Protocol: protocol
 Possible values are:
  - ip: ipv4
  - ipv6
  - dual
 Default is ip if not set.

All parameters are prefixed with internet to be able to extend this plugin
to other type of contexts (MMS, ...).

Conflicts:
	ofono/Makefile.am
	ofono/plugins/file-provision.c
2017-11-08 18:20:49 +03:00
Christophe Ronco
7bcadcd300 qmimodem: fix sim file reading on MC7304
Command read_file_info on MC7304 always fails.
Using qmicli or AT command, I am able to read file info.
Qmicli command is:
qmicli -d /dev/cdc-wdm0 --uim-get-file-attributes=0x3f00,0x7fff,0x6fad
[(null)] Successfully got file '/dev/cdc-wdm0' attributes from the UIM:
Card result:
        SW1: '0x90'
        SW2: '0x00'
File attributes:
        File size: 4
        File ID: 28589
        File type: transparent
        Record size: 0
        Record count: 0
        Read security attributes: (always) (null)
        Write security attributes: (single) adm
        Increase security attributes: (always) (null)
        Deactivate security attributes: (single) adm
        Activate security attributes: (single) adm
        Raw:    62:17:82:02:41:21:83:02:6F:AD:8A:01:05:8B:...

After a check of parameters sent by qmimodem driver and qmicli,
the only difference is on parameter "Session Information".
Session type sent by qmimodem driver is 'Card on slot 1'.
Session type sent by qmicli command is 'primary-gw-provisioning'.
Changing session type in qmimodem driver fixed problem (on this modem).

Comparing with what is done by qmili command
2017-11-08 18:20:49 +03:00
Christophe Ronco
b3f8dc4a24 atmodem: use ATD99 to enter data state when needed
Some modems do not support AT+CGDATA="PPP",X to enter data state.
Use AT+CGDATA=? to detect these modems and for them use ATD*99***X#
to enter data state.
2017-11-08 18:20:49 +03:00
Christophe Ronco
38e3122217 gprs: fix error check of gprs_cid_alloc function
When there is no context id available, idmap_alloc and
gprs_cid_alloc return max + 1.
2017-11-08 18:20:49 +03:00
Piotr Haber
f7de0ab3ef telit: register SIM if it goes straight to ready
SIM without PIN goes straight to state 3 (INSERTED and READY)
on some modem/firmware versions.
2017-11-08 18:20:49 +03:00
Denis Kenzior
defe008062 rilmodem: Fix warning
drivers/rilmodem/stk.c: In function ‘ril_stk_probe’:
drivers/rilmodem/stk.c:210:18: error: ‘data’ is used uninitialized in
this function [-Werror=uninitialized]
  struct cb_data *cbd = cb_data_new(stk, NULL, data);
2017-11-08 18:20:49 +03:00
Denis Kenzior
ec930e17c8 AUTHORS: mention André's contributions 2017-11-08 18:20:49 +03:00
André Draszik
9f1731cffa plugins: telit: send AT&C0 on both channels
At least firmware version 12.00.405 on the UE910-EUR otherwise
closes the AT command port whenever the PPP connection is
dropped, and we'd be left in a funny state.
2017-11-08 18:20:49 +03:00
Antara Borwankar
ed8d55d2d5 ril_intel: Add support for stk 2017-11-08 18:20:49 +03:00
Antara Borwankar
7b6a461b83 rilmodem: Send needed RIL request for stk init
sending RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING after
stk atom is created so that modem will inititialize
sim toolkit by sending terminal profile to sim.
2017-11-08 18:20:49 +03:00
Piotr Haber
3b0ff8fd83 telit: add support for UE866 2017-11-08 18:20:49 +03:00
Piotr Haber
00b5886cf9 udevng: unify telit and xe910
Unified setup for Telit devices using
usbserial and cdc_acm drivers.
2017-11-08 18:20:49 +03:00
Piotr Haber
c16fd4e642 plugins: rename xe910 to telit
Unify old telit and xe910 plugins.
Dropping support for Bluetooth SAP.
Add support for UC864 family.
2017-11-08 18:20:49 +03:00
Christophe Ronco
4b92ac8ba6 qmimodem: change kernel driver data format if needed
During gprs-context driver probe function, check kernel device driver
data format and modem low level data format.
If they are different, align kernel device driver data format on
modem low level data format.
If an error occurs during this process, continue probing without error.
This is inspired by what is done in qmicli and qmi-network (package
libqmi).
2017-11-08 18:20:49 +03:00
Denis Kenzior
5b432b8280 qmi: Fix typo 2017-11-08 18:20:49 +03:00
Christophe Ronco
31aff54463 qmimodem: get/set kernel device driver data format
Add a way to get and set data format expected by kernel device driver.
This is inspired by what is done in qmicli (package libqmi).
It does not use QMI protocol but a sysfs exported by kernel driver.
To use this feature, kernel version must be equal or more than 4.5.
2017-11-08 18:20:49 +03:00
Denis Kenzior
c669ec3c88 tools: Remove unused qmi tool 2017-11-08 18:20:49 +03:00
Piotr Haber
8c2f54abe0 xe910: support for Telit LE910V2 modem
LE910V2 is next generation Telit LTE modem.
It supports 3GPP Rel. 9 LTE Cat. 4 over multiple bands.
2017-11-08 18:20:49 +03:00
Piotr Haber
804121cbed telitmodem: support for CDC-NCM network adapter
Network Control Model is a new Communication Device Class
protocol for exchanging Ethernet frames over USB.
NCM is intended to be used with high-speed network
attachments such as HSDPA and LTE data services.
2017-11-08 18:20:49 +03:00
Piotr Haber
839e626ee6 udevng: setup of Telit LE910V2
Setup LE910V2 in default USB composition (1bc7:0036)
with 6 CDC-ACM serial ports and 1 CDC-NCM network adapter.
2017-11-08 18:20:49 +03:00
Piotr Haber
3a17724136 plugins: Handle HE910 and UE910 variants
Telit modems HE910 and UE910 share the same USB
vendor and device IDs (1bc7:0021) but they are
different devices.
HE910 is HSPA Class 14/6 and UE910 is Class 8/6.
Both come in voice-enabled variants.
HE910 also comes in variants with built-in GPS.
2017-11-08 18:20:49 +03:00
Piotr Haber
7b0c6610e0 plugins: rename he910 to xe910
In preparation for more generic support
of Telit xE910 family.
2017-11-08 18:20:49 +03:00
Piotr Haber
0e0b1e98c5 qmimodem: send authentication credentials
Pass authentication method, user and password
to QMI WDS service.
2017-11-08 18:20:48 +03:00
Piotr Haber
b0975c44b1 doc: description of Telit LE910V2 network setup
NCM network interface on LE910V2 modem needs to be
configured in a specific way after connection is established.
2017-11-08 18:20:48 +03:00
Denis Kenzior
25a6049cf6 doc: Add 'switch' to item M1 2017-11-08 18:20:48 +03:00
Denis Kenzior
02fcbdb245 AUTHORS: Mention Piotr's contributions 2017-11-08 18:20:48 +03:00
Piotr Haber
a3a8ea4183 atmodem: add LTE to Telit #PSNT status parser
Network type of 4 in Telit #PSNT command response
denotes LTE network
2017-11-08 18:20:48 +03:00
Vincent Cesson
92d7fb848b gemalto: Add location reporting support 2017-11-08 18:20:48 +03:00
Vincent Cesson
0b6327a7fc drivers: Add gemaltomodem driver to handle GNSS
Add a new location-reporting driver for Gemalto Cinterion modems based
on telit implementation + use it in gemalto plugin. It supports
activation of GNSS engine (Request) with command AT^SGPSC. This driver
is tested on PHS8. NMEA frames are accessible on /dev/ttyUSB1.
2017-11-08 18:20:48 +03:00
Vincent Cesson
84bd588152 udevng: complete gemalto setup
Complete the Gemalto setup with cdc_wdm and qmi,
so all enumerated devices are associated with gemalto.
2017-11-08 18:20:48 +03:00
Christophe Ronco
9e952cf042 qmimodem: query_passwd_state can be retried
Retry command QMI_UIM_GET_CARD_STATUS during query_passwd_state if a
temporary error status has been detected.
This happens with a MC7430 modem when query_passwd_state is called just
after PIN is entered.
2017-11-08 18:20:48 +03:00
Vincent Cesson
27a1a05aa7 udevng: Add Gemalto P-family detection
Add a new function setup, based on telit, to handle Gemalto P-family
discovery. The setup looks for USB interfaces:
application=/dev/ttyUSB2
gps=/dev/ttyUSB1
modem=/dev/ttyUSB3
2017-11-08 18:20:48 +03:00
Vincent Cesson
657841e2b0 plugins: Add Gemalto plugin for Cinterion P-family
Actual cinterion plugin is not compliant with newer Gemalto modems.
Gemalto plugin is based on cinterion with a custom struct to handle the
interfaces Application and Modem.
2017-11-08 18:20:48 +03:00
Denis Kenzior
02172f6922 AUTHORS: Mention Vincent's contributions 2017-11-08 18:20:48 +03:00
Vincent Cesson
0dd225b594 atmodem: Fix CGDCONT result parsing.
CGDCONT result parsing fails if first list contains several ranges. For
example with modem Cinterion PHS8:

 AT+CGDCONT=?
 +CGDCONT: (1-17,101-116),"IP",,,(0),(0-4)

Solution: read first range and jump to second list instead of trying to
close the brackets.
2017-11-08 18:20:48 +03:00
Christophe Ronco
452d0d4b5a qmimodem: add pin_send feature
Add ability to send PIN to a QMI modem using QMI_UIM_VERIFY_PIN command.
This has been tested on MC7304 and MC7430 modems.
2017-11-08 18:20:48 +03:00
Christophe Ronco
2edae61c0b qmimodem: get password state from modem
Password state and number of retries asked to modem using
QMI_UIM_GET_CARD_STATUS command rather than remembered after initial
QMI_UIM_GET_CARD_STATUS command.
2017-11-08 18:20:48 +03:00
Christophe Ronco
141abd5390 udevng: Sierra modems use SIM driver 2017-11-08 18:20:48 +03:00
Christophe Ronco
22faa0f26a qmimodem: Add read_imsi to qmimodem sim driver
Add read_imsi feature to qmimodem sim driver.
This is based on DMS service.
On MC7430, this is mandatory to be able to use this driver for GPRS
connection because reading IMSI via EF reads fails.
2017-11-08 18:20:48 +03:00
Christophe Ronco
4d2453f3a8 sim: backup driver read_imsi by IMSI via EF read
If read_imsi driver function fails, try to obtain IMSI via EF read
2017-11-08 18:20:48 +03:00
Slava Monich
7a5f52c1f3 sim: Stop facility lock query sequence on removal
SIM card can be removed while the query is in progress. There's
still a remote possibility that SIM card is removed and inserted
back while the query is pending, that would start the second query
sequence and end up invoking sim_initialize() twice. But at least
these checks reduce the probability of something like that happening.
2017-11-08 18:20:48 +03:00
Christophe Ronco
1ad109f8c7 qmimodem: fix QMI notification messages handling
QMI notification messages handlers are never called on MC7430 without this fix.

- Do not test transaction id before calling notification handler.  On MC7430,
notification messages contain a not null transaction id (starts with 1,
increased at each message for a particular client).
- On MC7304 transaction id in notification messages is always 0.
2017-11-08 18:20:48 +03:00
Denis Kenzior
1347755b6f AUTHORS: Mention Christophe's contributions 2017-11-08 18:20:48 +03:00
Christophe Ronco
62253744a7 qmimodem: fix get signal strength
Get current signal strength (type: 0x01), not list of other signals strength
(0x10)

Without this fix:
- I can't get a signal strength on MC7430 because list does not exist
	(only one signal strength).
- On MC7304, result is wrong
2017-11-08 18:20:48 +03:00
Marko Šulejić
9a608210cd atmodem: fix typo 2017-11-08 18:20:48 +03:00
Djalal Harouni
adbfdb23a7 test:netmon: support EARFCN, Eband and CQI in testing scripts 2017-11-08 18:20:48 +03:00
Djalal Harouni
ac5d0abe5e netmon: handle OFONO_NETMON_INFO_{EARFCN|EBAND|CQI} 2017-11-08 18:20:48 +03:00
Djalal Harouni
8dbaaa5efe doc: document netmon EARFCN, EBand and CQI properties
Documentation for:
EARFCN: E-UTRA Absolute Radio Frequency Channel Number.
EBand: E-UTRA operating Band.
CQI: Channel Quality Indicator.
2017-11-08 18:20:48 +03:00
Djalal Harouni
fa1bcc1c19 netmon: add NETMON_INFO_{EARFCN|EBAND|CQI}
Add the following types to use with ublox modems +UCGED command.

EARFCN: E-UTRA Absolute Radio Frequency Channel Number.
EBand: E-UTRA operating Band.
CQI: Channel Quality Indicator.
2017-11-08 18:20:48 +03:00
Antara Borwankar
32138ecd04 ril_intel: changes for lte modem
plugin modified to bring up lte atom and set MODEM_PROP_LTE_CAPABLE
to TRUE
2017-11-08 18:20:48 +03:00
Antara Borwankar
5e999f0b47 ril_intel: renamed ril_sofia3gr to ril_intel
renamed ril_sofia3gr.c to ril_intel.c
2017-11-08 18:20:47 +03:00
Ankit Navik
5c74095f44 rilmodem: set LTE preferred in rat mode 2017-11-08 18:20:47 +03:00
Ankit Navik
55befb87cd rilmodem: Add lte atom driver
Adds rilmodem driver for setting the default APN command.
The default APN is manage by config storage.
2017-11-08 18:20:47 +03:00
Denis Kenzior
9d7a0f8615 ubloxmodem: Fixup minor style issues
- useless return at the end of a function
- mixed tabs & spaces for indentation
- over 80 characters for the comment
- line > 80 characters due to access technology conversion.  Replaced
  with a direct assignment.
2017-11-08 18:20:47 +03:00
Denis Kenzior
974100732c ubloxmodem: Fix warning
drivers/ubloxmodem/netmon.c: In function ‘req_cb_data_unref’:
drivers/ubloxmodem/netmon.c:131:10: error: ‘return’ with a value, in
function returning void [-Werror]
   return NULL;
2017-11-08 18:20:47 +03:00
Djalal Harouni
35a6a4d8d0 build: build the ublox netmon driver 2017-11-08 18:20:47 +03:00
Djalal Harouni
f2a64c4d15 ubloxmodem: register and initialize the netmon driver 2017-11-08 18:20:47 +03:00
Djalal Harouni
ed1e90990e ubloxmodem: add the netmon driver
This adds a netmon driver for ublox. The driver support both +COPS and
+CESQ commands to return the previously added ofono netmon types:

RSCP: Received Signal Code Power
ECN0: Received Energy Ratio
RSRQ: Reference Signal Received Quality
RSRP: Reference Signal Received Power

Current revision makes the driver use ref countig when chaining multiple
AT commands.
2017-11-08 18:20:47 +03:00
Denis Kenzior
c8a4727243 AUTHORS: Mention Djalal's contributions 2017-11-08 18:20:47 +03:00
Djalal Harouni
2ccabbbdef test: support NETMON_INFO_{RXLEV|RSCP|ECN0|RSRQ|RSRP}
Display the following fields if they are returned.

RXLEV:  Received Signal Strength
RSCP:   Received Signal Code Power
ECN0:   Received Energy Ratio
RSRQ:   Reference Signal Received Quality
RSRP:   Reference Signal Received Power
2017-11-08 18:20:47 +03:00
Djalal Harouni
7c3638143d netmon: handle NETMON_INFO_{RSCP|ECN0|RSRQ|RSRP}
Handle the newly added netmon info types.
2017-11-08 18:20:47 +03:00
Djalal Harouni
a0e8b24c70 doc: Add additional netmon properties
This adds documentation for the following fields in
networkmonitor-api.txt

RSCP: Received Signal Code Power
ECN0: Received Energy Ratio
RSRQ: Reference Signal Received Quality
RSRP: Reference Signal Received Power
2017-11-08 18:20:47 +03:00
Djalal Harouni
41d432211e include: add NETMON_INFO_{RSCP|ECN0|RSRQ|RSRP}
Add more ofono netmon info types that will be served through the netmon
interface. The main user of this now will be the ublox modem.

RSCP: Received Signal Code Power
ECN0: Received Energy Ratio
RSRQ: Reference Signal Received Quality
RSRP: Reference Signal Received Power
2017-11-08 18:20:47 +03:00
Denis Kenzior
94f6138e23 netmon: Fix style issue 2017-11-08 18:20:47 +03:00
Denis Kenzior
e82ce81858 netmon: No need to initialize mcc/mnc 2017-11-08 18:20:47 +03:00
Dragos Tatulea
c18fa5e038 gprs: lte: set attached after successful activation
Otherwise the attached state gets to be set before the actual LTE
automatic context is ready. This triggers a race between connman
and ofono: connman sees status attached before the context is active
so connman will try to activate another context with same apn and will
fail over and over again.
2017-11-08 18:20:47 +03:00
Dragos Tatulea
c7c53adbb5 lte: fix early imsi free
storage_close was creating an empty sync file in /var/lib/ofono/lte
when it should have been closing the proper file
/var/lig/ofono/<imsi>/lte.
2017-11-08 18:20:47 +03:00
Slava Monich
30a9ef7e7a Sync whitespaces with upstream
Conflicts:
	ofono/src/smsutil.c
2017-11-08 18:20:47 +03:00
Dragos Tatulea
064181f903 ubloxmodem: tweak to work on different firmware
The U-Blox documentation specifies the following:
* get interface ip and dns from +CGDCONTRDP
* get gw ip and netmask from +UIPADDR

However, different firmware versions have different befaviour:

* On newer firmware, +UIPADDR returns error. But it's possible to configure
gateway ip == ipterface ip (read from CGDCONTRDP).

* On older firmware, we can actually use the commands specified in the
docs.

This patch runs +CGDCONTRDP, configures everything and then tries to run
+UIPADDR. In that works, reconfigures gw ip and netmask.
2017-11-08 18:20:47 +03:00
Dragos Tatulea
7d22ed86f8 plugins: ublox: enable lte driver for tobyl2 2017-11-08 18:20:47 +03:00
Dragos Tatulea
0641a981d1 build: add support for ublox lte atom driver 2017-11-08 18:20:47 +03:00
Dragos Tatulea
ceb6741a67 ubloxmodem: add lte atom driver
Adds U-Blox Toby L2 driver for setting the default APN via the
+UCGDFLT command. Currently only IPv4 is supported. APN is
not stored to modem's non-volatile memory. oFono will manage this
default APN via it's config storage.

When receiving an empty default APN, the value is reset.
2017-11-08 18:20:47 +03:00
Denis Kenzior
4797cab10b lte: If method is NULL, return not implemented 2017-11-08 18:20:47 +03:00
Denis Kenzior
fbf001bbec lte: Relax condition to g_free imsi
imsi malloc is not directly tied to l_settings being valid
2017-11-08 18:20:47 +03:00
Dragos Tatulea
8e90e96509 build: add lte atom support
Conflicts:
	ofono/Makefile.am
2017-11-08 18:20:47 +03:00
Dragos Tatulea
80e9b97036 lte: add implementation for LTE atom
This implementation can only get/set the default APN setting. But
anything expected for this atom is there:
* D-Bus interface
* sync-ing settings to/from file
* interaction with driver

Conflicts:
	ofono/src/ofono.h
2017-11-08 18:20:47 +03:00
Dragos Tatulea
01103f32ae ubloxmodem: fix memory leak in gprs_context_remove 2017-11-08 18:20:47 +03:00
Denis Kenzior
4bef0c7b33 include: Fix return signature in lte methods
Only the probe method returns a value.  All other methods use a void
return signature and report errors via the callback.
2017-11-08 18:20:47 +03:00
Denis Kenzior
693d5a77bd build: add test/set-lte-property 2017-11-08 18:20:47 +03:00
Denis Kenzior
a2333ead45 build: Add lte-api.txt 2017-11-08 18:20:46 +03:00
Dragos Tatulea
aa6a436af5 doc: add lte atom documentation 2017-11-08 18:20:46 +03:00
Dragos Tatulea
ee350d6b4b test: add script for setting lte atom properties 2017-11-08 18:20:46 +03:00
Denis Kenzior
26b85c0606 include: Make lte method const correct 2017-11-08 18:20:46 +03:00
Dragos Tatulea
068190a7a5 include: add header file for lte atom 2017-11-08 18:20:46 +03:00
Dragos Tatulea
1b292f7cf2 include: add LTE dbus service define 2017-11-08 18:20:46 +03:00
Kuba Pawlak
cd9a19c090 bluez5: fix crash on DBus transport disconnection
Do not register dbus notification if message failed to be sent out
because transport was already disconnected
2017-11-08 18:20:46 +03:00
Kuba Pawlak
6d357e70a4 gatchat: Fix parsing fields with odd number of quotation marks
Events like +CLCC and +CCWA can have contact name attached to the
end of line. If this field contains odd number of quotation marks,
parser will eventually reject such message as malformatted.
2017-11-08 18:20:46 +03:00
Dragos Tatulea
8f4817106d src: add LTE atom define 2017-11-08 18:20:46 +03:00
Slava Monich
d2ce689008 [ofono] Moved ofono_gprs_get_roaming_allowed declaration from gprs.h to ofono.h
.. primarily to make gprs.h identical to upstream. Besides, it's an internal
function, it doesn't have to be defined in gprs.h and exported from ofono in
the first place.
2017-11-08 18:12:15 +03:00
Slava Monich
b470166c87 [ril] Fixed -Wsign-compare compilation warnings
Upstream doesn't have those, let's keep our code clean, too.
2017-11-03 12:12:37 +03:00
Slava Monich
158a0da0b2 sim: Move atom registration to the end of ofono_sim_register
The state needs to be checked prior to calling __ofono_atom_register
because atom registration calls OFONO_ATOM_WATCH_CONDITION_REGISTERED
callbacks each of which may call ofono_sim_inserted_notify. Should
that happen, by the time __ofono_atom_register returns, ofono_sim
will be in OFONO_SIM_STATE_INSERTED state and sim_initialize will
be called twice if the initial state was OFONO_SIM_STATE_NOT_PRESENT.
If nothing else, that results in memory leaks like this one (because
IMSI will be queried twice, among other things):

==3017== 16 bytes in 1 blocks are definitely lost in loss record 187 of 475
==3017==    at 0x483F380: malloc (vg_replace_malloc.c:296)
==3017==    by 0x4AFB0DF: g_malloc (gmem.c:94)
==3017==    by 0x4B12185: g_strdup (gstrfuncs.c:363)
==3017==    by 0xF79D3: sim_imsi_obtained (sim.c:1535)
==3017==    by 0xF7BB3: sim_imsi_cb (sim.c:1594)
==3017==    by 0x66C23: at_cimi_cb (sim.c:441)
==3017==    by 0xA6B53: at_chat_finish_command (gatchat.c:459)
==3017==    by 0xA6D9F: at_chat_handle_command_response (gatchat.c:521)
==3017==    by 0xA70AF: have_line (gatchat.c:600)
==3017==    by 0xA76DF: new_bytes (gatchat.c:759)
==3017==    by 0xABACF: received_data (gatio.c:122)
==3017==    by 0xAD093: watch_dispatch (gatmux.c:461)
==3017==    by 0xAC5D3: dispatch_sources (gatmux.c:180)
==3017==    by 0xAC98F: received_data (gatmux.c:265)
==3017==    by 0x4AF606F: g_main_dispatch (gmain.c:3154)
==3017==    by 0x4AF606F: g_main_context_dispatch (gmain.c:3769)
==3017==    by 0x4AF631D: g_main_context_iterate.isra.4 (gmain.c:3840)
==3017==    by 0x4AF658F: g_main_loop_run (gmain.c:4034)
==3017==    by 0xBE8AF: main (main.c:261)
2017-10-27 19:10:19 +03:00
Slava Monich
b4bbf0462c gatchat: Removed unused GAtPPP field 2017-10-27 01:09:42 +03:00
Slava Monich
d80b96790f atmodem: Query the list of supported <fac>s from the modem
Not all modems support all <fac>s (particularly, "PS"), let's be polite
and not ask them for the ones they don't support.
2017-10-24 02:07:17 +03:00
Slava Monich
accb571fd6 gatmux: Remove write watch source at shutdown
Otherwise write_watcher_destroy_notify can be invoked after
GAtMux has been deallocated which results in write after free:

==3952== Invalid write of size 4
==3952==    at 0xABF54: write_watcher_destroy_notify (gatmux.c:285)
==3952==    by 0x4AF21E7: g_source_callback_unref (gmain.c:1561)
==3952==    by 0x4AF2E53: g_source_destroy_internal.constprop.8 (gmain.c:1207)
==3952==    by 0x4AF61CF: g_main_dispatch (gmain.c:3177)
==3952==    by 0x4AF61CF: g_main_context_dispatch (gmain.c:3769)
==3952==    by 0x4AF658F: g_main_loop_run (gmain.c:4034)
==3952==    by 0xBDDBB: main (main.c:261)
==3952==  Address 0x50c6cb0 is 8 bytes inside a block of size 4,396 free'd
==3952==    at 0x4840B28: free (vg_replace_malloc.c:530)
==3952==    by 0xACB53: g_at_mux_unref (gatmux.c:642)
==3952==  Block was alloc'd at
==3952==    at 0x4841BF0: calloc (vg_replace_malloc.c:711)
==3952==    by 0xAC9DF: g_at_mux_new (gatmux.c:603)
==3952==    by 0xADF2F: g_at_mux_new_gsm0710_basic (gatmux.c:1160)
2017-10-24 02:07:16 +03:00
Slava Monich
523a4b6a81 gatmux: Remove finalized watches from the list
Leaving them there may result in invalid reads like this:

==2312== Invalid read of size 4
==2312==    at 0xAB8C0: dispatch_sources (gatmux.c:134)
==2312==    by 0xAC5D3: channel_close (gatmux.c:479)
==2312==    by 0x4AE8885: g_io_channel_shutdown (giochannel.c:523)
==2312==    by 0x4AE8A1D: g_io_channel_unref (giochannel.c:240)
==2312==    by 0xAC423: watch_finalize (gatmux.c:426)
==2312==    by 0x4AF2CC9: g_source_unref_internal (gmain.c:2048)
==2312==    by 0x4AF44E1: g_source_destroy_internal (gmain.c:1230)
==2312==    by 0x4AF44E1: g_source_destroy (gmain.c:1256)
==2312==    by 0x4AF5257: g_source_remove (gmain.c:2282)
==2312==    by 0xAB5CB: io_shutdown (gatio.c:325)
==2312==    by 0xAB667: g_at_io_unref (gatio.c:345)
==2312==    by 0xA72C7: at_chat_unref (gatchat.c:972)
==2312==    by 0xA829B: g_at_chat_unref (gatchat.c:1446)
==2312==  Address 0x51420f0 is 56 bytes inside a block of size 60 free'd
==2312==    at 0x4840B28: free (vg_replace_malloc.c:530)
==2312==    by 0x4AF2D33: g_source_unref_internal (gmain.c:2075)
==2312==    by 0x4AF44E1: g_source_destroy_internal (gmain.c:1230)
==2312==    by 0x4AF44E1: g_source_destroy (gmain.c:1256)
==2312==    by 0x4AF5257: g_source_remove (gmain.c:2282)
==2312==    by 0xAB46B: g_at_io_set_write_handler (gatio.c:283)
==2312==    by 0xA713F: at_chat_suspend (gatchat.c:938)
==2312==    by 0xA72B7: at_chat_unref (gatchat.c:971)
==2312==    by 0xA829B: g_at_chat_unref (gatchat.c:1446)
==2312==  Block was alloc'd at
==2312==    at 0x4841BF0: calloc (vg_replace_malloc.c:711)
==2312==    by 0x4AFB117: g_malloc0 (gmem.c:124)
==2312==    by 0x4AF401F: g_source_new (gmain.c:892)
==2312==    by 0xAC6A7: channel_create_watch (gatmux.c:506)
==2312==    by 0x4AE7C4F: g_io_add_watch_full (giochannel.c:649)
==2312==    by 0xAB4EB: g_at_io_set_write_handler (gatio.c:297)
==2312==    by 0xA7103: chat_wakeup_writer (gatchat.c:931)
==2312==    by 0xA753F: at_chat_send_common (gatchat.c:1045)
==2312==    by 0xA850F: g_at_chat_send (gatchat.c:1502)

It's also necessary to add additional references to the sources
for the duration of the dispatch_sources loop because any source
can be removed when any callback is invoked (and not necessarily
the one being dispatched).
2017-10-24 00:07:47 +03:00
Slava Monich
9d8a6a4978 Fixed -Wsign-compare warnings
unit/test-sailfish_sim_info.c:86:33: signed and unsigned type in conditional expression
drivers/ril/ril_netmon.c:46:12: comparison between signed and unsigned integer expressions
2017-10-23 23:56:29 +03:00
Slava Monich
f2fa85aa47 [ril] Housekeeping
Took out some leftovers of the past
2017-10-20 18:13:17 +03:00
Slava Monich
a189d13b4a Merge branch 'dbus-queue' into 'master'
Generic queueing of D-Bus requests

See merge request !151
2017-10-19 08:17:38 +00:00
Slava Monich
3b79a77d78 [ofono] radio-settings: Use ofono_dbus_queue for queuing D-Bus requests
Instead of failing with org.ofono.Error.InProgress, requests
are queued and handled in the order they are received.
2017-10-18 23:56:40 +03:00
Slava Monich
c2ee34e51c [ofono] network: Use ofono_dbus_queue for queuing D-Bus requests 2017-10-18 22:39:51 +03:00
Slava Monich
c3bead1c9b [ofono] Generic queueing of D-Bus requests
Instead of failing with org.ofono.Error.InProgress, requests may be queued.
2017-10-18 22:39:21 +03:00
Slava Monich
a26f1a4b5c [ril] Removed a few unused things 2017-10-18 12:43:31 +03:00
Slava Monich
1eacfdf592 [ril] First fix permissions on top-level directories. JB#39961
... and then descend into subdirectories. Even though it doesn't
really matter since we are starting as root, it does seem to be
a bit more logical.
2017-10-13 18:28:42 +03:00
Slava Monich
ba14ed43e4 Merge branch 'perm' into 'master'
Fix storage directory permissions at startup

See merge request !150
2017-10-13 14:33:21 +00:00
Slava Monich
802b3008be [ril] Fix storage directory permissions at startup. Fixes JB#39961
Also, made the identity configurable and got rid of hardcoded radio
uid and gid, those are now queried at runtime.
2017-10-13 17:16:24 +03:00
Slava Monich
3b0191d145 [test] Added test-ril_util 2017-10-13 17:07:26 +03:00
Slava Monich
282d32f70d [ril] Added ril_parse_int utility 2017-10-13 17:05:43 +03:00
Slava Monich
e0c349a18c Merge branch 'smsfilter' into 'master'
SMS filter plugin

See merge request !138
2017-10-10 13:00:08 +00:00
Slava Monich
69d65dc002 [ofono] Support for SMS filter plugins. JB#37478 2017-10-10 15:49:28 +03:00
Slava Monich
33e70ddce4 [ofono] Allow older versions of plugins.
Let's assume that plugin API is going to be backward compatible. If
we start to actually use binary ofono plugins (possibly, 3rd-party),
backward compatibility becomes a must.
2017-10-10 15:49:28 +03:00
Slava Monich
d3ada8fcb3 [ofono] Fixed setting/clearing debug flags for binary plugins
They were added to the list of debug categories but their flags
were not actually getting updated on client's requests.
2017-10-10 15:48:59 +03:00
Slava Monich
4c21ca4e26 atmodem: Fix use after free in sim_state_cb
==2941== Invalid read of size 4
==2941==    at 0x69338: sim_state_cb (sim.c:1301)
==2941==    by 0x71DCB: cpin_check_cb (atutil.c:567)
==2941==    by 0xA602B: at_chat_finish_command (gatchat.c:459)
==2941==    by 0xA6277: at_chat_handle_command_response (gatchat.c:521)
==2941==    by 0xA6587: have_line (gatchat.c:600)
==2941==    by 0xA6BB7: new_bytes (gatchat.c:759)
==2941==    by 0xAAFAF: received_data (gatio.c:124)
==2941==    by 0x4AF606F: g_main_dispatch (gmain.c:3154)
==2941==    by 0x4AF606F: g_main_context_dispatch (gmain.c:3769)
==2941==    by 0x4AF658F: g_main_loop_run (gmain.c:4034)
==2941==    by 0xBDDBB: main (main.c:261)
==2941==  Address 0x519c344 is 4 bytes inside a block of size 12 free'd
==2941==    at 0x4840B28: free (vg_replace_malloc.c:530)
==2941==    by 0x71F33: at_util_sim_state_query_free (atutil.c:613)
==2941==    by 0x6930B: sim_state_cb (sim.c:1297)
==2941==    by 0x71DCB: cpin_check_cb (atutil.c:567)
==2941==    by 0xA602B: at_chat_finish_command (gatchat.c:459)
==2941==    by 0xA6277: at_chat_handle_command_response (gatchat.c:521)
==2941==    by 0xA6587: have_line (gatchat.c:600)
==2941==    by 0xA6BB7: new_bytes (gatchat.c:759)
==2941==    by 0xAAFAF: received_data (gatio.c:124)
==2941==    by 0x4AF606F: g_main_dispatch (gmain.c:3154)
==2941==    by 0x4AF606F: g_main_context_dispatch (gmain.c:3769)
==2941==    by 0x4AF658F: g_main_loop_run (gmain.c:4034)
==2941==    by 0xBDDBB: main (main.c:261)
2017-10-05 19:46:32 +03:00
Slava Monich
a3301ec1d2 modem: Implement ofono_modem_get_sim 2017-10-05 19:46:31 +03:00
Slava Monich
6f11bfc632 include: Add ofono_modem_get_sim 2017-10-05 19:46:31 +03:00
Slava Monich
74262b9ef8 [ril] Don't mix slice and default allocators in ril_plugin.c
And it generally doesn't make sense to use slice allocator for allocating
the structures that are a) large and b) allocated at startup and not freed
until the program exits.
2017-10-02 17:31:47 +03:00
Slava Monich
199a610607 [ril] Fixed erroneous assert 2017-10-02 16:34:33 +03:00
Slava Monich
af2d223f0f [ofono] Moved gutil_log_func initialization from ril_plugin_init()
.. to debuglog_init(). There's no reason for it to be in RIL specific code.
2017-10-02 13:31:26 +03:00
Slava Monich
0b6fcf8b71 [mbpi] Use PROVIDER_DATABASE from config.h as the default
It's pulled from mobile-broadband-provider-info.pc by the
configure script, we should trust it.
2017-10-02 12:48:23 +03:00
Slava Monich
cc05aeccd1 Merge branch 'debug' into 'master'
Fix logging issues with external plugins

See merge request !149
2017-10-01 22:14:36 +00:00
Slava Monich
5728444ad3 [iofono] Allow to manage logs of binary plugins
External (binary) plugins have __debug section of their own.
2017-09-30 00:28:54 +03:00
Slava Monich
4cbb6b5919 [ofono] Added __ofono_plugin_foreach()
Invokes a callback for each registered plugin. It's needed by debuglog
plugin because external plugins have __debug section of their own and
debuglog needs access to debug_start and debug_stop of such plugins
in order to enable or disable plugin logs.
2017-09-29 21:27:35 +03:00
Slava Monich
5699bb4932 [build] Fixed configure check for libmce-glib
Version 1.0.5 is required for mce_display_remove_all_handlers macro
2017-09-29 13:06:34 +03:00
Slava Monich
09fa97c53a Merge branch 'default_timeout' into 'master'
Use default start timeout for default configuration

See merge request !148
2017-09-25 06:46:56 +00:00
Slava Monich
3eaa8a46bd [ril] Use default start timeout for default configuration. Fixes JB#39840
If /etc/ofono/ril_subscription.conf doesn't exist or doesn't contain
any modem sections (the latter is the case on Jolla 1 for historical
reasons) ofono falls back to the default configuration which should
use the default start timeout.
2017-09-23 18:07:57 +03:00
Slava Monich
4401319136 Merge branch 'ready' into 'master'
Set the ready flag if no drivers is registered with sailfish_manager

See merge request !147
2017-09-15 15:48:25 +00:00
Slava Monich
472ddcf0b1 Merge branch 'optionalmodem' into 'master'
Support for optional modems

See merge request !119
2017-09-15 15:48:09 +00:00
Slava Monich
b7e0f276a1 [ril] Support for optional modems. Fixes MER#1783 2017-09-15 17:14:57 +03:00
Slava Monich
5d251aea3a [ofono] More unit tests for the ready flag 2017-09-14 15:03:57 +03:00
Slava Monich
cdc0065284 [ofono] Set the ready flag if no drivers is registered with sailfish_manager. Fixes MER#1808
If there are no supported modems, ofono should be happy and the ready
flag should become true because the UI is waiting for this flag at
startup (and in this case there's nothing to wait for).
2017-09-14 15:01:45 +03:00
Slava Monich
6d65dc5bf0 Merge branch 'cellinfo' into 'master'
Move cell info API to the driver independent area.

See merge request !146
2017-09-14 08:39:03 +00:00
Slava Monich
5d02c0bba4 [ofono] Added unit test for sailfish_cell_info 2017-09-14 00:32:32 +03:00
Slava Monich
b99513e080 [ofono] Move cell info API to the driver independent area. Fixes JB#39725
So that it could be used by any driver, not just by RIL.
2017-09-14 00:29:13 +03:00
Slava Monich
3cf328c781 Merge branch 'ack' into 'master'
Support for logging new packet types (RIL v13)

See merge request !145
2017-09-12 13:36:33 +00:00
Slava Monich
bce68611a1 [ril] Support for logging new packet types (RIL v13). JB#39228 2017-09-12 00:18:52 +03:00
Slava Monich
cc497feee7 Merge branch 'online' into 'master'
Fix online state tracking

See merge request !143
2017-09-06 10:18:23 +00:00
Matti Kosola
725606af8d Merge branch 'jb39622' into 'master'
Handle normal call end with error cause.

See merge request !144
2017-09-06 08:58:25 +00:00
Juho Hämäläinen
52db6e5459 [ofono] Handle normal call end with error cause. Fixes JB#39622
With multi-sim setup we get last cause code 0xffff when incoming
call is ending due to other multi-sim phone answering the call.
Due to this handle the 0xffff as remote hangup if call status is
incoming.
2017-09-06 11:51:57 +03:00
Slava Monich
83441bc203 [ril] Fixed online state tracking. Fixes JB#39592 2017-09-05 12:14:52 +03:00
129 changed files with 10377 additions and 3446 deletions

8
ofono/.gitignore vendored
View File

@@ -42,12 +42,15 @@ unit/test-mux
unit/test-caif
unit/test-stkutil
unit/test-cdmasms
unit/test-ril_util
unit/test-rilmodem-cb
unit/test-rilmodem-cs
unit/test-rilmodem-gprs
unit/test-rilmodem-sms
unit/test-sailfish_cell_info
unit/test-sailfish_manager
unit/test-sailfish_sim_info
unit/test-sms-filter
unit/test-*.log
unit/test-*.trs
@@ -57,9 +60,14 @@ unit/test-grilunsol
unit/test-provision
unit/html
plugins/sailfish_manager/*.gcda
plugins/sailfish_manager/*.gcno
drivers/*/*.gcda
drivers/*/*.gcno
drivers/*/*.gcov
plugins/*/*.gcda
plugins/*/*.gcno
plugins/*/*.gcov
*/*.gcda
*/*.gcno
*/*.gcov

View File

@@ -116,3 +116,10 @@ Martin Chaplet <m.chaplet@kerlink.fr>
Suman Mallela <suman.m@intel.com>
Rajagopal Aravindan <rajagopalx.aravindan@intel.com>
Antoine Aubert <a.aubert@overkiz.com>
Djalal Harouni <djalal@endocode.com>
Christophe Ronco <c.ronco@kerlink.fr>
Vincent Cesson <vincent.cesson@smile.fr>
Piotr Haber <gluedig@gmail.com>
André Draszik <git@andred.net>
Lukasz Nowak <lnowak@tycoint.com>
Jonas Bonn <jonas@southpole.se>

View File

@@ -1,3 +1,23 @@
ver 1.20:
Fix issue with context removal before activation.
Fix issue with update during GPRS context activation.
Fix issue with receiving UTF-16 encoded messages.
Fix issue with invalid access in CBS decoding.
Fix issue with signal strength on QMI modems.
Fix issue with PIN handling with QMI modems.
Fix issue with QMI notification message handling.
Fix issue with facility lock query on SIM removal.
Fix issue with parsing +CLCC and +CCWA fields.
Add support for obtaining IMSI via EF reading.
Add support for additional netmon info types.
Add support for provisioning via configuration files.
Add support for Gemalto P-family series of modems.
Add support for Telit HE910 and UE910 variants.
Add support for Intel SoFIA SIM Toolkit interfaces.
Add support for Intel SoFIA LTE features.
Add support for U-Blox TOBY-L2 LTE feature.
Add support for dedicated LTE atom.
ver 1.19:
Fix issue with DHCP parsing and Huawei modems.
Fix issue with detecting Huawei E3372 modem.

View File

@@ -23,13 +23,14 @@ pkginclude_HEADERS = include/log.h include/plugin.h include/history.h \
include/cdma-provision.h include/handsfree.h \
include/sim-mnclength.h \
include/handsfree-audio.h include/siri.h \
include/netmon.h
include/sms-filter.h \
include/netmon.h include/lte.h
nodist_pkginclude_HEADERS = include/version.h
if SAILFISH_MANAGER
nodist_pkginclude_HEADERS += include/sailfish_manager.h \
include/sailfish_watch.h
nodist_pkginclude_HEADERS += include/sailfish_cell_info.h \
include/sailfish_manager.h include/sailfish_watch.h
endif
local_headers = $(foreach file,$(pkginclude_HEADERS) \
@@ -112,8 +113,6 @@ gril_sources = gril/gril.h gril/gril.c \
btio_sources = btio/btio.h btio/btio.c
if UDEV
builtin_modules += udev
builtin_sources += plugins/udev.c
builtin_cflags += @UDEV_CFLAGS@
builtin_libadd += @UDEV_LIBS@
@@ -123,7 +122,9 @@ endif
if SAILFISH_MANAGER
builtin_modules += sailfish_manager
builtin_sources += plugins/sailfish_manager/sailfish_manager.c \
builtin_sources += plugins/sailfish_manager/sailfish_cell_info.c \
plugins/sailfish_manager/sailfish_cell_info_dbus.c \
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 \
@@ -139,7 +140,6 @@ builtin_sources += drivers/ril/ril_call_barring.c \
drivers/ril/ril_call_settings.c \
drivers/ril/ril_call_volume.c \
drivers/ril/ril_cell_info.c \
drivers/ril/ril_cell_info_dbus.c \
drivers/ril/ril_config.c \
drivers/ril/ril_cbs.c \
drivers/ril/ril_data.c \
@@ -183,8 +183,8 @@ builtin_sources += plugins/ril.c plugins/ril.h
builtin_modules += infineon
builtin_sources += plugins/infineon.c
builtin_modules += ril_sofia3gr
builtin_sources += plugins/ril_sofia3gr.c
builtin_modules += ril_intel
builtin_sources += plugins/ril_intel.c
builtin_modules += rilmodem
builtin_sources += drivers/rilmodem/rilmodem.h \
@@ -209,7 +209,8 @@ builtin_sources += drivers/rilmodem/rilmodem.h \
drivers/rilmodem/netmon.c \
drivers/rilmodem/stk.c \
drivers/rilmodem/cbs.c \
drivers/infineonmodem/infineon_constants.h
drivers/infineonmodem/infineon_constants.h \
drivers/rilmodem/lte.c
endif
endif
@@ -268,11 +269,13 @@ qmi_sources = drivers/qmimodem/qmi.h drivers/qmimodem/qmi.c \
drivers/qmimodem/ctl.h \
drivers/qmimodem/dms.h \
drivers/qmimodem/nas.h \
drivers/qmimodem/nas.c \
drivers/qmimodem/uim.h \
drivers/qmimodem/wms.h \
drivers/qmimodem/wds.h \
drivers/qmimodem/pds.h \
drivers/qmimodem/common.h
drivers/qmimodem/common.h \
drivers/qmimodem/wda.h
builtin_modules += qmimodem
builtin_sources += $(qmi_sources) \
@@ -377,7 +380,8 @@ builtin_modules += telitmodem
builtin_sources += drivers/atmodem/atutil.h \
drivers/telitmodem/telitmodem.h \
drivers/telitmodem/telitmodem.c \
drivers/telitmodem/location-reporting.c
drivers/telitmodem/location-reporting.c \
drivers/telitmodem/gprs-context-ncm.c
builtin_modules += hsomodem
builtin_sources += drivers/atmodem/atutil.h \
@@ -439,7 +443,16 @@ builtin_modules += ubloxmodem
builtin_sources += drivers/atmodem/atutil.h \
drivers/ubloxmodem/ubloxmodem.h \
drivers/ubloxmodem/ubloxmodem.c \
drivers/ubloxmodem/gprs-context.c
drivers/ubloxmodem/gprs-context.c \
drivers/ubloxmodem/netmon.c \
drivers/ubloxmodem/lte.c
builtin_modules += gemaltomodem
builtin_sources += drivers/atmodem/atutil.h \
drivers/gemaltomodem/gemaltomodem.h \
drivers/gemaltomodem/gemaltomodem.c \
drivers/gemaltomodem/location-reporting.c
if PHONESIM
@@ -506,6 +519,9 @@ builtin_sources += plugins/caif.c
builtin_modules += cinterion
builtin_sources += plugins/cinterion.c
builtin_modules += gemalto
builtin_sources += plugins/gemalto.c
builtin_modules += nokia
builtin_sources += plugins/nokia.c
@@ -533,6 +549,12 @@ builtin_sources += plugins/samsung.c
builtin_modules += sim900
builtin_sources += plugins/sim900.c
builtin_modules += connman
builtin_sources += plugins/connman.c
builtin_modules += telit
builtin_sources += plugins/telit.c
builtin_modules += quectel
builtin_sources += plugins/quectel.c
@@ -546,11 +568,11 @@ endif
builtin_modules += connman
builtin_sources += plugins/connman.c
builtin_modules += mnclength
builtin_sources += plugins/mnclength.c
if BLUETOOTH
if BLUEZ4
builtin_modules += telit
builtin_sources += plugins/telit.c plugins/bluez4.h
builtin_modules += sap
builtin_sources += plugins/sap.c plugins/bluez4.h
@@ -627,8 +649,9 @@ builtin_sources += plugins/provision.h
builtin_modules += cdma_provision
builtin_sources += plugins/cdma-provision.c
builtin_modules += mnclength
builtin_sources += plugins/mnclength.c
builtin_modules += file_provision
builtin_sources += plugins/file-provision.c
endif
if MAINTAINER_MODE
@@ -700,8 +723,9 @@ src_ofonod_SOURCES = $(builtin_sources) $(gatchat_sources) src/ofono.ver \
src/cdma-provision.c src/handsfree.c \
src/handsfree-audio.c src/bluetooth.h \
src/sim-mnclength.c src/voicecallagent.c \
src/sms-filter.c src/dbus-queue.c \
src/hfp.h src/siri.c \
src/netmon.c
src/netmon.c src/lte.c
src_ofonod_LDADD = gdbus/libgdbus-internal.la $(builtin_libadd) \
@GLIB_LIBS@ @DBUS_LIBS@ -ldl
@@ -749,7 +773,8 @@ doc_files = doc/overview.txt doc/ofono-paper.txt doc/release-faq.txt \
doc/certification.txt doc/siri-api.txt \
doc/telit-modem.txt \
doc/networkmonitor-api.txt \
doc/allowed-apns-api.txt
doc/allowed-apns-api.txt \
doc/lte-api.txt
test_scripts = test/backtrace \
@@ -856,7 +881,8 @@ test_scripts = test/backtrace \
test/get-serving-cell-info \
test/list-allowed-access-points \
test/enable-throttling \
test/disable-throttling
test/disable-throttling \
test/set-lte-property
if TEST
testdir = $(pkglibdir)/test
@@ -877,10 +903,18 @@ unit_objects =
unit_tests = unit/test-common unit/test-util unit/test-idmap \
unit/test-simutil unit/test-stkutil \
unit/test-sms unit/test-cdmasms \
unit/test-provision
unit/test-provision unit/test-sms-filter
if SAILFISH_MANAGER
unit_test_sailfish_cell_info_SOURCES = unit/test-sailfish_cell_info.c \
plugins/sailfish_manager/sailfish_cell_info.c
unit_test_sailfish_cell_info_CFLAGS = $(AM_CFLAGS) $(COVERAGE_OPT) \
-Iplugins/sailfish_cell_info
unit_test_sailfish_cell_info_LDADD = @GLIB_LIBS@ -ldl
unit_objects += $(unit_test_sailfish_cell_info_OBJECTS)
unit_tests += unit/test-sailfish_cell_info
unit_test_sailfish_sim_info_SOURCES = unit/test-sailfish_sim_info.c \
unit/fake_sailfish_watch.c \
plugins/sailfish_manager/sailfish_sim_info.c \
@@ -894,6 +928,7 @@ 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_cell_info.c \
plugins/sailfish_manager/sailfish_sim_info.c \
src/storage.c src/log.c
unit_test_sailfish_manager_CFLAGS = $(AM_CFLAGS) $(COVERAGE_OPT) \
@@ -907,6 +942,14 @@ endif
if RILMODEM
if SAILFISH_RILMODEM
unit_test_ril_util_SOURCES = unit/test-ril_util.c drivers/ril/ril_util.c \
src/log.c
unit_test_ril_util_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
unit_test_ril_util_LDADD = @GLIB_LIBS@ -ldl
unit_objects += $(unit_test_ril_util_OBJECTS)
unit_tests += unit/test-ril_util
else
unit_tests += unit/test-rilmodem-cs \
unit/test-rilmodem-cs \
unit/test-rilmodem-sms \
@@ -983,6 +1026,12 @@ unit_test_provision_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
unit_test_provision_LDADD = @GLIB_LIBS@ -ldl
unit_objects += $(unit_test_provision_OBJECTS)
unit_test_sms_filter_SOURCES = unit/test-sms-filter.c \
src/sms-filter.c src/log.c
unit_test_sms_filter_CFLAGS = $(COVERAGE_OPT) $(AM_CFLAGS)
unit_test_sms_filter_LDADD = @GLIB_LIBS@ -ldl
unit_objects += $(unit_test_sms_filter_OBJECTS)
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 \
@@ -1046,13 +1095,6 @@ tools_lookup_provider_name_LDADD = @GLIB_LIBS@
tools_tty_redirector_SOURCES = tools/tty-redirector.c
tools_tty_redirector_LDADD = @GLIB_LIBS@
if QMIMODEM
noinst_PROGRAMS += tools/qmi
tools_qmi_SOURCES = $(qmi_sources) tools/qmi.c
tools_qmi_LDADD = @GLIB_LIBS@
endif
if MAINTAINER_MODE
noinst_PROGRAMS += tools/stktest

View File

@@ -1,5 +1,5 @@
AC_PREREQ(2.60)
AC_INIT(ofono, 1.19)
AC_INIT(ofono, 1.20)
AM_INIT_AUTOMAKE([foreign subdir-objects color-tests])
AC_CONFIG_HEADERS(config.h)
@@ -184,12 +184,12 @@ AC_ARG_ENABLE(sailfish-rilmodem, AC_HELP_STRING([--enable-sailfish-rilmodem],
AM_CONDITIONAL(SAILFISH_RILMODEM, test "${enable_sailfish_rilmodem}" != "no")
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(GRILIO, libgrilio >= 1.0.18, dummy=yes,
AC_MSG_ERROR(libgrilio >= 1.0.18 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))
PKG_CHECK_MODULES(LIBMCE, libmce-glib >= 1.0.5, dummy=yes,
AC_MSG_ERROR(libmce-glib >= 1.0.5 is required))
CFLAGS="$CFLAGS $GRILIO_CFLAGS $LIBMCE_CFLAGS"
LIBS="$LIBS $GRILIO_LIBS $LIBMCE_LIBS"
enable_sailfish_manager=yes

View File

@@ -19,7 +19,7 @@ Besides the kernel coding style above, oFono has special flavors for its own.
Some of them are mandatory (marked as 'M'), while some others are optional
(marked as 'O'), but generally preferred.
M1: Blank line before and after an if/while/do/for statement
M1: Blank line before and after an if/while/do/for/switch statement
============================================================
There should be a blank line before if statement unless the if is nested and
not preceded by an expression or variable declaration.

View File

@@ -76,6 +76,22 @@ Methods dict GetProperties()
[service].Error.NotImplemented
[service].Error.NotAllowed
fd, byte Acquire()
Attempts to establish the SCO audio connection
returning the filedescriptor of the connection and the
codec in use.
Note: Contrary to Connect this does not call
NewConnection so it can be called in a blocking
manner.
Possible Errors: [service].Error.InProgress
[service].Error.Failed
[service].Error.NotAvailable
[service].Error.NotImplemented
[service].Error.NotAllowed
Signals PropertyChanged(string name, variant value)
This signal indicates a changed value of the given

35
ofono/doc/lte-api.txt Normal file
View File

@@ -0,0 +1,35 @@
LongTermEvolution Hierarchy
Service org.ofono
Interface org.ofono.LongTermEvolution
Object path [variable prefix]/{modem0,modem1,...}
Methods dict GetProperties()
Returns all LongTermEvolution configuration properties.
void SetProperty(string property, variant value)
Changes the value of the specified property. Only
properties that are listed as readwrite are
changeable. On success a PropertyChanged signal
will be emitted.
Possible Errors: [service].Error.InProgress
[service].Error.InvalidArguments
[service].Error.Failed
Signals PropertyChanged(string property, variant value)
This signal indicates a changed value of the given
property.
Properties string DefaultAccessPointName [readwrite]
On LongTermEvolution, contexts activate automatically.
This property allows selection of an APN to be used on
next automatic activation.
Setting this property to an empty string clears the
default APN from the modem.

View File

@@ -81,3 +81,42 @@ byte Strength [optional, gsm, umts]
Contains the signal strength. Valid values are 0-31. Refer to <rssi>
in 27.007, Section 8.5.
byte ReceivedSignalCodePower [optional, umts]
Contains the Received Signal Code Power. Valid range of values
is 0-96. Refer to <rscp> in 27.007, Section 8.69 for more details.
byte ReceivedEnergyRatio [optional, umts]
Contains the Ratio of received energy per PN chip to the total
received power spectral density. Valid range of values is 0-49.
Refer to <ecno> in 27.007, Section 8.69 for more details.
byte ReferenceSignalReceivedQuality [optional, lte]
Contains the Reference Signal Received Quality. Valid range of
values is 0-34. Refer to <rsrq> in 27.007, Section 8.69 for more
details.
byte ReferenceSignalReceivedPower [optional, lte]
Contains the Reference Signal Received Power. Valid range of values
is 0-97. Refer to <rsrp> in 27.007, Section 8.69 for more details.
uint16 EARFCN [optional, lte]
Contains E-UTRA Absolute Radio Frequency Channel Number. Valid
range of values is 0-65535. Refer to Carrier frequency and
EARFCN in 36.101, Section 5.7.3 for more details.
byte EBand [optional, lte]
Contains E-UTRA operating Band. Valid range of values is 1-43.
Refer to Operating bands in 36.101, Section 5.5 for more
details.
byte ChannelQualityIndicator [optional, lte]
Contains Channel Quality Indicator. Refer to Channel Quality
Indicator definition in 36.213, Section 7.2.3 for more details.

View File

@@ -17,3 +17,30 @@ GPS:
After setting the configuration, a power cycle is required.
Port Configiuration #8 is available since firmware 12.00.004. Firmware version
can be checked using 'AT+CGMR'.
LE910 V2
========
Default USB composition of LE910V2 uses PID 0x36 (AT#PORTCFG=0)
and consists of 6 serial ports (CDC-ACM standard, /dev/ttyACMx)
and 1 network adapter using CDC-NCM standard (wwanx or usbx).
NCM interface configuration follows Telit documentation
(both documents available on Telit Download Zone - registration required)
"GE/HE/UE910, UL865, LE910 V2 Linux USB Driver - User Guide r0"
(document 1VV0301255 Rev.0 - 2016-01-22)
and "Telit LE910-V2 NCM SETUP r3"
(document 1VV0301246 Rev.3 - 2016-11-29).
After context is setup, NCM mode activated and PDP context activated
connection configuration can be read using
AT+CGPADDR=context_id and AT+CGCONTRDP=context_id commands.
This is done automatically and results available via
org.ofono.ConnectionContext.GetProperties DBus method.
Then Linux network interface needs to be configured:
ifconfig <Interface> <Address> netmask <Netmask> up
route add default gw <Gateway>
arp -s <Gateway> 11:22:33:44:55:66
Only after these steps network interface is usable.

View File

@@ -47,6 +47,7 @@
#define STATIC_IP_NETMASK "255.255.255.255"
static const char *cgdata_prefix[] = { "+CGDATA:", NULL };
static const char *none_prefix[] = { NULL };
enum state {
@@ -67,6 +68,7 @@ struct gprs_context_data {
ofono_gprs_context_cb_t cb;
void *cb_data; /* Callback data */
unsigned int vendor;
gboolean use_atd99;
};
static void ppp_debug(const char *str, void *data)
@@ -210,7 +212,7 @@ static void at_cgdcont_cb(gboolean ok, GAtResult *result, gpointer user_data)
return;
}
if (gcd->vendor == OFONO_VENDOR_SIMCOM_SIM900)
if (gcd->use_atd99)
sprintf(buf, "ATD*99***%u#", gcd->active_context);
else
sprintf(buf, "AT+CGDATA=\"PPP\",%u", gcd->active_context);
@@ -382,6 +384,43 @@ static void cgev_notify(GAtResult *result, gpointer user_data)
g_at_ppp_shutdown(gcd->ppp);
}
static void at_cgdata_test_cb(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct ofono_gprs_context *gc = user_data;
struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
GAtResultIter iter;
const char *data_type;
gboolean found = FALSE;
gcd->use_atd99 = TRUE;
if (!ok) {
DBG("not ok");
goto error;
}
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CGDATA:")) {
DBG("no +CGDATA line");
goto error;
}
if (!g_at_result_iter_open_list(&iter)) {
DBG("no list found");
goto error;
}
while (!found && g_at_result_iter_next_string(&iter, &data_type)) {
if (g_str_equal(data_type, "PPP")) {
found = TRUE;
gcd->use_atd99 = FALSE;
}
}
error:
DBG("use_atd99:%d", gcd->use_atd99);
}
static int at_gprs_context_probe(struct ofono_gprs_context *gc,
unsigned int vendor, void *data)
{
@@ -409,6 +448,15 @@ static int at_gprs_context_probe(struct ofono_gprs_context *gc,
if (chat == NULL)
return 0;
switch (vendor) {
case OFONO_VENDOR_SIMCOM_SIM900:
gcd->use_atd99 = FALSE;
break;
default:
g_at_chat_send(chat, "AT+CGDATA=?", cgdata_prefix,
at_cgdata_test_cb, gc, NULL);
}
g_at_chat_register(chat, "+CGEV:", cgev_notify, FALSE, gc, NULL);
return 0;

View File

@@ -50,6 +50,8 @@ struct gprs_data {
GAtChat *chat;
unsigned int vendor;
unsigned int last_auto_context_id;
gboolean telit_try_reattach;
int attached;
};
static void at_cgatt_cb(gboolean ok, GAtResult *result, gpointer user_data)
@@ -73,8 +75,10 @@ static void at_gprs_set_attached(struct ofono_gprs *gprs, int attached,
snprintf(buf, sizeof(buf), "AT+CGATT=%i", attached ? 1 : 0);
if (g_at_chat_send(gd->chat, buf, none_prefix,
at_cgatt_cb, cbd, g_free) > 0)
at_cgatt_cb, cbd, g_free) > 0) {
gd->attached = attached;
return;
}
g_free(cbd);
@@ -194,6 +198,28 @@ static void cgreg_notify(GAtResult *result, gpointer user_data)
NULL, NULL, NULL, gd->vendor) == FALSE)
return;
/*
* Telit AT modem firmware (tested with UE910-EUR) generates
* +CGREG: 0\r\n\r\n+CGEV: NW DETACH
* after a context is de-activated and ppp connection closed.
* Then, after a random amount of time (observed from a few seconds
* to a few hours), an unsolicited +CGREG: 1 arrives.
* Attempt to fix the problem, by sending AT+CGATT=1 once.
* This does not re-activate the context, but if a network connection
* is still correct, will generate an immediate +CGREG: 1.
*/
if (gd->vendor == OFONO_VENDOR_TELIT) {
if (gd->attached && !status && !gd->telit_try_reattach) {
DBG("Trying to re-attach gprs network");
gd->telit_try_reattach = TRUE;
g_at_chat_send(gd->chat, "AT+CGATT=1", none_prefix,
NULL, NULL, NULL);
return;
}
gd->telit_try_reattach = FALSE;
}
ofono_gprs_status_notify(gprs, status);
}
@@ -214,6 +240,11 @@ static void cgev_notify(GAtResult *result, gpointer user_data)
if (g_str_equal(event, "NW DETACH") ||
g_str_equal(event, "ME DETACH")) {
if (gd->vendor == OFONO_VENDOR_TELIT &&
gd->telit_try_reattach)
return;
gd->attached = FALSE;
ofono_gprs_detached_notify(gprs);
return;
} else if (g_str_has_prefix(event, "ME PDN ACT")) {
@@ -323,6 +354,9 @@ static void telit_mode_notify(GAtResult *result, gpointer user_data)
case 3:
bearer = 5; /* HSDPA */
break;
case 4:
bearer = 7; /* LTE */
break;
default:
bearer = 0;
break;
@@ -522,7 +556,7 @@ static void at_cgdcont_test_cb(gboolean ok, GAtResult *result,
if (g_at_result_iter_next_range(&iter, &min, &max) == FALSE)
continue;
if (!g_at_result_iter_close_list(&iter))
if (!g_at_result_iter_skip_next(&iter))
continue;
if (g_at_result_iter_open_list(&iter))

View File

@@ -51,6 +51,7 @@ struct sim_data {
GAtChat *chat;
unsigned int vendor;
guint ready_id;
guint passwd_type_mask;
struct at_util_sim_state_query *sim_state_query;
};
@@ -1293,14 +1294,15 @@ static void sim_state_cb(gboolean present, gpointer user_data)
struct cb_data *cbd = user_data;
struct sim_data *sd = cbd->user;
ofono_sim_lock_unlock_cb_t cb = cbd->cb;
void *data = cbd->data;
at_util_sim_state_query_free(sd->sim_state_query);
sd->sim_state_query = NULL;
if (present == 1)
CALLBACK_WITH_SUCCESS(cb, cbd->data);
CALLBACK_WITH_SUCCESS(cb, data);
else
CALLBACK_WITH_FAILURE(cb, cbd->data);
CALLBACK_WITH_FAILURE(cb, data);
}
static void at_pin_send_cb(gboolean ok, GAtResult *result,
@@ -1458,9 +1460,8 @@ static void at_pin_enable(struct ofono_sim *sim,
struct cb_data *cbd = cb_data_new(cb, data);
char buf[64];
int ret;
unsigned int len = sizeof(at_clck_cpwd_fac) / sizeof(*at_clck_cpwd_fac);
if (passwd_type >= len || at_clck_cpwd_fac[passwd_type] == NULL)
if (!(sd->passwd_type_mask & (1 << passwd_type)))
goto error;
snprintf(buf, sizeof(buf), "AT+CLCK=\"%s\",%i,\"%s\"",
@@ -1489,10 +1490,8 @@ static void at_change_passwd(struct ofono_sim *sim,
struct cb_data *cbd = cb_data_new(cb, data);
char buf[64];
int ret;
unsigned int len = sizeof(at_clck_cpwd_fac) / sizeof(*at_clck_cpwd_fac);
if (passwd_type >= len ||
at_clck_cpwd_fac[passwd_type] == NULL)
if (!(sd->passwd_type_mask & (1 << passwd_type)))
goto error;
snprintf(buf, sizeof(buf), "AT+CPWD=\"%s\",\"%s\",\"%s\"",
@@ -1549,9 +1548,8 @@ static void at_query_clck(struct ofono_sim *sim,
struct sim_data *sd = ofono_sim_get_data(sim);
struct cb_data *cbd = cb_data_new(cb, data);
char buf[64];
unsigned int len = sizeof(at_clck_cpwd_fac) / sizeof(*at_clck_cpwd_fac);
if (passwd_type >= len || at_clck_cpwd_fac[passwd_type] == NULL)
if (!(sd->passwd_type_mask & (1 << passwd_type)))
goto error;
snprintf(buf, sizeof(buf), "AT+CLCK=\"%s\",2",
@@ -1567,13 +1565,42 @@ error:
CALLBACK_WITH_FAILURE(cb, -1, data);
}
static gboolean at_sim_register(gpointer user)
static void at_clck_query_cb(gboolean ok, GAtResult *result, gpointer user)
{
struct ofono_sim *sim = user;
struct sim_data *sd = ofono_sim_get_data(sim);
GAtResultIter iter;
const char *fac;
if (!ok)
goto done;
g_at_result_iter_init(&iter, result);
/* e.g. +CLCK: ("SC","FD","PN","PU","PP","PC","PF") */
if (!g_at_result_iter_next(&iter, "+CLCK:") ||
!g_at_result_iter_open_list(&iter))
goto done;
/* Clear the default mask */
sd->passwd_type_mask = 0;
/* Set the bits for <fac>s that are actually supported */
while (g_at_result_iter_next_string(&iter, &fac)) {
unsigned int i;
/* Find it in the list of known <fac>s */
for (i = 0; i < ARRAY_SIZE(at_clck_cpwd_fac); i++) {
if (!g_strcmp0(at_clck_cpwd_fac[i], fac)) {
sd->passwd_type_mask |= (1 << i);
DBG("found %s", fac);
break;
}
}
}
done:
ofono_sim_register(sim);
return FALSE;
}
static int at_sim_probe(struct ofono_sim *sim, unsigned int vendor,
@@ -1581,6 +1608,7 @@ static int at_sim_probe(struct ofono_sim *sim, unsigned int vendor,
{
GAtChat *chat = data;
struct sim_data *sd;
unsigned int i;
sd = g_new0(struct sim_data, 1);
sd->chat = g_at_chat_clone(chat);
@@ -1590,9 +1618,15 @@ static int at_sim_probe(struct ofono_sim *sim, unsigned int vendor,
g_at_chat_send(sd->chat, "AT*EPEE=1", NULL, NULL, NULL, NULL);
ofono_sim_set_data(sim, sd);
g_idle_add(at_sim_register, sim);
return 0;
/* <fac>s supported by default */
for (i = 0; i < ARRAY_SIZE(at_clck_cpwd_fac); i++)
if (at_clck_cpwd_fac[i])
sd->passwd_type_mask |= (1 << i);
/* Query supported <fac>s */
return g_at_chat_send(sd->chat, "AT+CLCK=?", clck_prefix,
at_clck_query_cb, sim, NULL) ? 0 : -1;
}
static void at_sim_remove(struct ofono_sim *sim)

View File

@@ -319,26 +319,6 @@ static void at_cnma_cb(gboolean ok, GAtResult *result, gpointer user_data)
"Further SMS reception is not guaranteed");
}
static gboolean at_parse_cmt(GAtResult *result, const char **pdu, int *pdulen)
{
GAtResultIter iter;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CMT:"))
return FALSE;
if (!g_at_result_iter_skip_next(&iter))
return FALSE;
if (!g_at_result_iter_next_number(&iter, pdulen))
return FALSE;
*pdu = g_at_result_pdu(result);
return TRUE;
}
static inline void at_ack_delivery(struct ofono_sms *sms)
{
struct sms_data *data = ofono_sms_get_data(sms);
@@ -347,11 +327,21 @@ static inline void at_ack_delivery(struct ofono_sms *sms)
DBG("");
/* We must acknowledge the PDU using CNMA */
if (data->cnma_ack_pdu)
snprintf(buf, sizeof(buf), "AT+CNMA=1,%d\r%s",
data->cnma_ack_pdu_len, data->cnma_ack_pdu);
else /* Should be a safe fallback */
if (data->cnma_ack_pdu) {
switch (data->vendor) {
case OFONO_VENDOR_CINTERION:
snprintf(buf, sizeof(buf), "AT+CNMA=1");
break;
default:
snprintf(buf, sizeof(buf), "AT+CNMA=1,%d\r%s",
data->cnma_ack_pdu_len,
data->cnma_ack_pdu);
break;
}
} else {
/* Should be a safe fallback */
snprintf(buf, sizeof(buf), "AT+CNMA=0");
}
g_at_chat_send(data->chat, buf, none_prefix, at_cnma_cb, NULL, NULL);
}
@@ -409,16 +399,34 @@ static void at_cmt_notify(GAtResult *result, gpointer user_data)
{
struct ofono_sms *sms = user_data;
struct sms_data *data = ofono_sms_get_data(sms);
GAtResultIter iter;
const char *hexpdu;
unsigned char pdu[176];
long pdu_len;
int tpdu_len;
unsigned char pdu[176];
if (!at_parse_cmt(result, &hexpdu, &tpdu_len)) {
ofono_error("Unable to parse CMT notification");
return;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CMT:"))
goto err;
switch (data->vendor) {
case OFONO_VENDOR_CINTERION:
if (!g_at_result_iter_next_number(&iter, &tpdu_len))
goto err;
break;
default:
if (!g_at_result_iter_skip_next(&iter))
goto err;
if (!g_at_result_iter_next_number(&iter, &tpdu_len))
goto err;
break;
}
hexpdu = g_at_result_pdu(result);
if (strlen(hexpdu) > sizeof(pdu) * 2) {
ofono_error("Bad PDU length in CMT notification");
return;
@@ -431,6 +439,9 @@ static void at_cmt_notify(GAtResult *result, gpointer user_data)
if (data->vendor != OFONO_VENDOR_SIMCOM)
at_ack_delivery(sms);
err:
ofono_error("Unable to parse CMT notification");
}
static void at_cmgr_notify(GAtResult *result, gpointer user_data)
@@ -742,7 +753,7 @@ static void at_sms_initialized(struct ofono_sms *sms)
static void at_sms_not_supported(struct ofono_sms *sms)
{
ofono_error("SMS not supported by this modem. If this is in error"
ofono_error("SMS not supported by this modem. If this is an error"
" please submit patches to support this hardware");
ofono_sms_remove(sms);

View File

@@ -0,0 +1,49 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 Vincent Cesson. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <glib.h>
#include <gatchat.h>
#define OFONO_API_SUBJECT_TO_CHANGE
#include <ofono/plugin.h>
#include <ofono/types.h>
#include "gemaltomodem.h"
static int gemaltomodem_init(void)
{
gemalto_location_reporting_init();
return 0;
}
static void gemaltomodem_exit(void)
{
gemalto_location_reporting_exit();
}
OFONO_PLUGIN_DEFINE(gemaltomodem, "Gemalto modem driver", VERSION,
OFONO_PLUGIN_PRIORITY_DEFAULT,
gemaltomodem_init, gemaltomodem_exit)

View File

@@ -2,7 +2,7 @@
*
* oFono - Open Source Telephony
*
* Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
* Copyright (C) 2017 Vincent Cesson. 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
@@ -19,11 +19,7 @@
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <drivers/atmodem/atutil.h>
int main(int argc, char **argv)
{
return 0;
}
extern void gemalto_location_reporting_init();
extern void gemalto_location_reporting_exit();

View File

@@ -0,0 +1,237 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 Vincent Cesson. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define _GNU_SOURCE
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <glib.h>
#include <ofono/log.h>
#include <ofono/modem.h>
#include <ofono/location-reporting.h>
#include "gatchat.h"
#include "gatresult.h"
#include "gattty.h"
#include "gemaltomodem.h"
static const char *sgpsc_prefix[] = { "^SGPSC:", NULL };
struct gps_data {
GAtChat *chat;
};
static void gemalto_gps_disable_cb(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct cb_data *cbd = user_data;
struct ofono_location_reporting *lr = cbd->user;
ofono_location_reporting_disable_cb_t cb = cbd->cb;
DBG("lr=%p, ok=%d", lr, ok);
if (!ok) {
struct ofono_error error;
decode_at_error(&error, g_at_result_final_response(result));
cb(&error, cbd->data);
return;
}
CALLBACK_WITH_SUCCESS(cb, cbd->data);
}
static void gemalto_location_reporting_disable(
struct ofono_location_reporting *lr,
ofono_location_reporting_disable_cb_t cb,
void *data)
{
struct gps_data *gd = ofono_location_reporting_get_data(lr);
struct cb_data *cbd = cb_data_new(cb, data);
DBG("lr=%p", lr);
cbd->user = lr;
if (g_at_chat_send(gd->chat, "AT^SGPSC=\"Engine\",0", sgpsc_prefix,
gemalto_gps_disable_cb, cbd, g_free) > 0)
return;
CALLBACK_WITH_FAILURE(cb, data);
g_free(cbd);
}
static int enable_data_stream(struct ofono_location_reporting *lr)
{
struct ofono_modem *modem;
const char *gps_dev;
GHashTable *options;
GIOChannel *channel;
int fd;
modem = ofono_location_reporting_get_modem(lr);
gps_dev = ofono_modem_get_string(modem, "GPS");
options = g_hash_table_new(g_str_hash, g_str_equal);
if (options == NULL)
return -1;
g_hash_table_insert(options, "Baud", "115200");
channel = g_at_tty_open(gps_dev, options);
g_hash_table_destroy(options);
if (channel == NULL)
return -1;
fd = g_io_channel_unix_get_fd(channel);
g_io_channel_set_close_on_unref(channel, FALSE);
g_io_channel_unref(channel);
return fd;
}
static void gemalto_sgpsc_cb(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_location_reporting_enable_cb_t cb = cbd->cb;
struct ofono_location_reporting *lr = cbd->user;
struct ofono_error error;
int fd;
DBG("lr=%p ok=%d", lr, ok);
decode_at_error(&error, g_at_result_final_response(result));
if (!ok) {
cb(&error, -1, cbd->data);
return;
}
fd = enable_data_stream(lr);
if (fd < 0) {
CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
return;
}
cb(&error, fd, cbd->data);
close(fd);
}
static void gemalto_location_reporting_enable(struct ofono_location_reporting *lr,
ofono_location_reporting_enable_cb_t cb,
void *data)
{
struct gps_data *gd = ofono_location_reporting_get_data(lr);
struct cb_data *cbd = cb_data_new(cb, data);
DBG("lr=%p", lr);
cbd->user = lr;
if (g_at_chat_send(gd->chat, "AT^SGPSC=\"Engine\",2", sgpsc_prefix,
gemalto_sgpsc_cb, cbd, NULL) > 0)
return;
CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
g_free(cbd);
}
static void gemalto_location_reporting_support_cb(gboolean ok, GAtResult *result,
gpointer user_data)
{
struct ofono_location_reporting *lr = user_data;
if (!ok) {
ofono_location_reporting_remove(lr);
return;
}
ofono_location_reporting_register(lr);
}
static int gemalto_location_reporting_probe(struct ofono_location_reporting *lr,
unsigned int vendor, void *data)
{
GAtChat *chat = data;
struct gps_data *gd;
gd = g_try_new0(struct gps_data, 1);
if (gd == NULL)
return -ENOMEM;
gd->chat = g_at_chat_clone(chat);
ofono_location_reporting_set_data(lr, gd);
g_at_chat_send(gd->chat, "AT^SGPSC=?", sgpsc_prefix,
gemalto_location_reporting_support_cb,
lr, NULL);
return 0;
}
static void gemalto_location_reporting_remove(struct ofono_location_reporting *lr)
{
struct gps_data *gd = ofono_location_reporting_get_data(lr);
ofono_location_reporting_set_data(lr, NULL);
g_at_chat_unref(gd->chat);
g_free(gd);
}
static struct ofono_location_reporting_driver driver = {
.name = "gemaltomodem",
.type = OFONO_LOCATION_REPORTING_TYPE_NMEA,
.probe = gemalto_location_reporting_probe,
.remove = gemalto_location_reporting_remove,
.enable = gemalto_location_reporting_enable,
.disable = gemalto_location_reporting_disable,
};
void gemalto_location_reporting_init()
{
ofono_location_reporting_driver_register(&driver);
}
void gemalto_location_reporting_exit()
{
ofono_location_reporting_driver_unregister(&driver);
}

View File

@@ -23,6 +23,8 @@
#include <config.h>
#endif
#include <string.h>
#include <ofono/log.h>
#include <ofono/modem.h>
#include <ofono/devinfo.h>
@@ -125,7 +127,8 @@ static void get_ids_cb(struct qmi_result *result, void *user_data)
}
str = qmi_result_get_string(result, QMI_DMS_RESULT_ESN);
if (!str) {
/* Telit qmi modems return a "0" string when ESN is not available. */
if (!str || strcmp(str, "0") == 0) {
str = qmi_result_get_string(result, QMI_DMS_RESULT_IMEI);
if (!str) {
CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);

View File

@@ -24,18 +24,22 @@
#endif
#include <string.h>
#include <arpa/inet.h>
#include <ofono/log.h>
#include <ofono/modem.h>
#include <ofono/gprs-context.h>
#include "qmi.h"
#include "wda.h"
#include "wds.h"
#include "qmimodem.h"
struct gprs_context_data {
struct qmi_service *wds;
struct qmi_service *wda;
struct qmi_device *dev;
unsigned int active_context;
uint32_t pkt_handle;
};
@@ -61,8 +65,12 @@ static void pkt_status_notify(struct qmi_result *result, void *user_data)
switch (status->status) {
case QMI_WDS_CONN_STATUS_DISCONNECTED:
ofono_gprs_context_deactivated(gc, data->active_context);
data->active_context = 0;
if (data->pkt_handle) {
/* The context has been disconnected by the network */
ofono_gprs_context_deactivated(gc, data->active_context);
data->pkt_handle = 0;
data->active_context = 0;
}
break;
}
}
@@ -75,18 +83,68 @@ static void get_settings_cb(struct qmi_result *result, void *user_data)
struct ofono_modem *modem;
const char *interface;
uint8_t pdp_type, ip_family;
uint32_t ip_addr;
struct in_addr addr;
char* straddr;
char* apn;
const char *dns[3] = { NULL, NULL, NULL };
DBG("");
if (qmi_result_set_error(result, NULL))
goto done;
apn = qmi_result_get_string(result, QMI_WDS_RESULT_APN);
if (apn) {
DBG("APN: %s", apn);
g_free(apn);
}
if (qmi_result_get_uint8(result, QMI_WDS_RESULT_PDP_TYPE, &pdp_type))
DBG("PDP type %d", pdp_type);
if (qmi_result_get_uint8(result, QMI_WDS_RESULT_IP_FAMILY, &ip_family))
DBG("IP family %d", ip_family);
if (qmi_result_get_uint32(result,QMI_WDS_RESULT_IP_ADDRESS, &ip_addr)) {
addr.s_addr = htonl(ip_addr);
straddr = inet_ntoa(addr);
DBG("IP addr: %s", straddr);
ofono_gprs_context_set_ipv4_address(gc, straddr, 1);
}
if (qmi_result_get_uint32(result,QMI_WDS_RESULT_GATEWAY, &ip_addr)) {
addr.s_addr = htonl(ip_addr);
straddr = inet_ntoa(addr);
DBG("Gateway: %s", straddr);
ofono_gprs_context_set_ipv4_gateway(gc, straddr);
}
if (qmi_result_get_uint32(result,
QMI_WDS_RESULT_GATEWAY_NETMASK, &ip_addr)) {
addr.s_addr = htonl(ip_addr);
straddr = inet_ntoa(addr);
DBG("Gateway netmask: %s", straddr);
ofono_gprs_context_set_ipv4_netmask(gc, straddr);
}
if (qmi_result_get_uint32(result,
QMI_WDS_RESULT_PRIMARY_DNS, &ip_addr)) {
addr.s_addr = htonl(ip_addr);
dns[0] = inet_ntoa(addr);
DBG("Primary DNS: %s", dns[0]);
}
if (qmi_result_get_uint32(result,
QMI_WDS_RESULT_SECONDARY_DNS, &ip_addr)) {
addr.s_addr = htonl(ip_addr);
dns[1] = inet_ntoa(addr);
DBG("Secondary DNS: %s", dns[1]);
}
if (dns[0])
ofono_gprs_context_set_ipv4_dns_servers(gc, dns);
done:
modem = ofono_gprs_context_get_modem(gc);
interface = ofono_modem_get_string(modem, "NetworkInterface");
@@ -94,8 +152,6 @@ done:
ofono_gprs_context_set_interface(gc, interface);
CALLBACK_WITH_SUCCESS(cb, cbd->data);
g_free(cbd);
}
static void start_net_cb(struct qmi_result *result, void *user_data)
@@ -120,8 +176,12 @@ static void start_net_cb(struct qmi_result *result, void *user_data)
data->pkt_handle = handle;
/* Duplicate cbd, the old one will be freed when this method returns */
cbd = cb_data_new(cb, cbd->data);
cbd->user = gc;
if (qmi_service_send(data->wds, QMI_WDS_GET_SETTINGS, NULL,
get_settings_cb, cbd, NULL) > 0)
get_settings_cb, cbd, g_free) > 0)
return;
modem = ofono_gprs_context_get_modem(gc);
@@ -131,12 +191,39 @@ static void start_net_cb(struct qmi_result *result, void *user_data)
CALLBACK_WITH_SUCCESS(cb, cbd->data);
g_free(cbd);
return;
error:
data->active_context = 0;
CALLBACK_WITH_FAILURE(cb, cbd->data);
}
/*
* This function gets called for "automatic" contexts, those which are
* not activated via activate_primary. For these, we will still need
* to call start_net in order to get the packet handle for the context.
* The process for automatic contexts is essentially identical to that
* for others.
*/
static void qmi_gprs_read_settings(struct ofono_gprs_context* gc,
unsigned int cid,
ofono_gprs_context_cb_t cb,
void *user_data)
{
struct gprs_context_data *data = ofono_gprs_context_get_data(gc);
struct cb_data *cbd = cb_data_new(cb, user_data);
DBG("cid %u", cid);
data->active_context = cid;
cbd->user = gc;
if (qmi_service_send(data->wds, QMI_WDS_START_NET, NULL,
start_net_cb, cbd, g_free) > 0)
return;
data->active_context = 0;
CALLBACK_WITH_FAILURE(cb, cbd->data);
@@ -151,6 +238,7 @@ static void qmi_activate_primary(struct ofono_gprs_context *gc,
struct cb_data *cbd = cb_data_new(cb, user_data);
struct qmi_param *param;
uint8_t ip_family;
uint8_t auth;
DBG("cid %u", ctx->cid);
@@ -178,8 +266,31 @@ static void qmi_activate_primary(struct ofono_gprs_context *gc,
qmi_param_append_uint8(param, QMI_WDS_PARAM_IP_FAMILY, ip_family);
switch (ctx->auth_method) {
case OFONO_GPRS_AUTH_METHOD_CHAP:
auth = QMI_WDS_AUTHENTICATION_CHAP;
break;
case OFONO_GPRS_AUTH_METHOD_PAP:
auth = QMI_WDS_AUTHENTICATION_PAP;
break;
default:
auth = QMI_WDS_AUTHENTICATION_NONE;
break;
}
qmi_param_append_uint8(param, QMI_WDS_PARAM_AUTHENTICATION_PREFERENCE,
auth);
if (ctx->username[0] != '\0')
qmi_param_append(param, QMI_WDS_PARAM_USERNAME,
strlen(ctx->username), ctx->username);
if (ctx->password[0] != '\0')
qmi_param_append(param, QMI_WDS_PARAM_PASSWORD,
strlen(ctx->password), ctx->password);
if (qmi_service_send(data->wds, QMI_WDS_START_NET, param,
start_net_cb, cbd, NULL) > 0)
start_net_cb, cbd, g_free) > 0)
return;
qmi_param_free(param);
@@ -202,17 +313,19 @@ static void stop_net_cb(struct qmi_result *result, void *user_data)
DBG("");
if (qmi_result_set_error(result, NULL)) {
CALLBACK_WITH_FAILURE(cb, cbd->data);
if (cb)
CALLBACK_WITH_FAILURE(cb, cbd->data);
return;
}
data->active_context = 0;
data->pkt_handle = 0;
CALLBACK_WITH_SUCCESS(cb, cbd->data);
if (cb)
CALLBACK_WITH_SUCCESS(cb, cbd->data);
else
ofono_gprs_context_deactivated(gc, data->active_context);
g_free(cbd);
data->active_context = 0;
}
static void qmi_deactivate_primary(struct ofono_gprs_context *gc,
@@ -233,17 +346,26 @@ static void qmi_deactivate_primary(struct ofono_gprs_context *gc,
goto error;
if (qmi_service_send(data->wds, QMI_WDS_STOP_NET, param,
stop_net_cb, cbd, NULL) > 0)
stop_net_cb, cbd, g_free) > 0)
return;
qmi_param_free(param);
error:
CALLBACK_WITH_FAILURE(cb, cbd->data);
if (cb)
CALLBACK_WITH_FAILURE(cb, user_data);
g_free(cbd);
}
static void qmi_gprs_context_detach_shutdown(struct ofono_gprs_context *gc,
unsigned int cid)
{
DBG("");
qmi_deactivate_primary(gc, cid, NULL, NULL);
}
static void create_wds_cb(struct qmi_service *service, void *user_data)
{
struct ofono_gprs_context *gc = user_data;
@@ -263,6 +385,69 @@ static void create_wds_cb(struct qmi_service *service, void *user_data)
pkt_status_notify, gc, NULL);
}
static void get_data_format_cb(struct qmi_result *result, void *user_data)
{
struct ofono_gprs_context *gc = user_data;
struct gprs_context_data *data = ofono_gprs_context_get_data(gc);
uint32_t llproto;
enum qmi_device_expected_data_format expected_llproto;
DBG("");
if (qmi_result_set_error(result, NULL))
goto done;
if (!qmi_result_get_uint32(result, QMI_WDA_LL_PROTOCOL, &llproto))
goto done;
expected_llproto = qmi_device_get_expected_data_format(data->dev);
if ((llproto == QMI_WDA_DATA_LINK_PROTOCOL_802_3) &&
(expected_llproto ==
QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP)) {
if (!qmi_device_set_expected_data_format(data->dev,
QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3))
DBG("Fail to set expected data to 802.3");
else
DBG("expected data set to 802.3");
} else if ((llproto == QMI_WDA_DATA_LINK_PROTOCOL_RAW_IP) &&
(expected_llproto ==
QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3)) {
if (!qmi_device_set_expected_data_format(data->dev,
QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP))
DBG("Fail to set expected data to raw-ip");
else
DBG("expected data set to raw-ip");
}
done:
qmi_service_create_shared(data->dev, QMI_SERVICE_WDS, create_wds_cb, gc,
NULL);
}
static void create_wda_cb(struct qmi_service *service, void *user_data)
{
struct ofono_gprs_context *gc = user_data;
struct gprs_context_data *data = ofono_gprs_context_get_data(gc);
DBG("");
if (!service) {
DBG("Failed to request WDA service, continue initialization");
goto error;
}
data->wda = qmi_service_ref(service);
if (qmi_service_send(data->wda, QMI_WDA_GET_DATA_FORMAT, NULL,
get_data_format_cb, gc, NULL) > 0)
return;
error:
qmi_service_create_shared(data->dev, QMI_SERVICE_WDS, create_wds_cb, gc,
NULL);
}
static int qmi_gprs_context_probe(struct ofono_gprs_context *gc,
unsigned int vendor, void *user_data)
{
@@ -274,8 +459,9 @@ static int qmi_gprs_context_probe(struct ofono_gprs_context *gc,
data = g_new0(struct gprs_context_data, 1);
ofono_gprs_context_set_data(gc, data);
data->dev = device;
qmi_service_create(device, QMI_SERVICE_WDS, create_wds_cb, gc, NULL);
qmi_service_create(device, QMI_SERVICE_WDA, create_wda_cb, gc, NULL);
return 0;
}
@@ -288,9 +474,15 @@ static void qmi_gprs_context_remove(struct ofono_gprs_context *gc)
ofono_gprs_context_set_data(gc, NULL);
qmi_service_unregister_all(data->wds);
if (data->wds) {
qmi_service_unregister_all(data->wds);
qmi_service_unref(data->wds);
}
qmi_service_unref(data->wds);
if (data->wda) {
qmi_service_unregister_all(data->wda);
qmi_service_unref(data->wda);
}
g_free(data);
}
@@ -301,6 +493,8 @@ static struct ofono_gprs_context_driver driver = {
.remove = qmi_gprs_context_remove,
.activate_primary = qmi_activate_primary,
.deactivate_primary = qmi_deactivate_primary,
.read_settings = qmi_gprs_read_settings,
.detach_shutdown = qmi_gprs_context_detach_shutdown,
};
void qmi_gprs_context_init(void)

View File

@@ -30,16 +30,18 @@
#include "qmi.h"
#include "nas.h"
#include "src/common.h"
#include "qmimodem.h"
struct gprs_data {
struct qmi_service *nas;
};
static bool extract_ss_info(struct qmi_result *result, int *status)
static bool extract_ss_info(struct qmi_result *result, int *status, int *tech)
{
const struct qmi_nas_serving_system *ss;
uint16_t len;
int i;
DBG("");
@@ -47,14 +49,46 @@ static bool extract_ss_info(struct qmi_result *result, int *status)
if (!ss)
return false;
if (ss->ps_state == QMI_NAS_ATTACH_STATUS_ATTACHED)
*status = 0x01;
if (ss->ps_state == QMI_NAS_ATTACH_STATE_ATTACHED)
*status = NETWORK_REGISTRATION_STATUS_REGISTERED;
else
*status = 0x00;
*status = NETWORK_REGISTRATION_STATUS_NOT_REGISTERED;
*tech = -1;
for (i = 0; i < ss->radio_if_count; i++) {
DBG("radio in use %d", ss->radio_if[i]);
*tech = qmi_nas_rat_to_tech(ss->radio_if[i]);
}
return true;
}
static int handle_ss_info(struct qmi_result *result, struct ofono_gprs *gprs)
{
int status;
int tech;
DBG("");
if (!extract_ss_info(result, &status, &tech))
return -1;
if (status == NETWORK_REGISTRATION_STATUS_REGISTERED)
if (tech == ACCESS_TECHNOLOGY_EUTRAN) {
/* On LTE we are effectively always attached; and
* the default bearer is established as soon as the
* network is joined.
*/
/* FIXME: query default profile number and APN
* instead of assuming profile 1 and ""
*/
ofono_gprs_cid_activated(gprs, 1 , "automatic");
}
return status;
}
static void ss_info_notify(struct qmi_result *result, void *user_data)
{
struct ofono_gprs *gprs = user_data;
@@ -62,10 +96,10 @@ static void ss_info_notify(struct qmi_result *result, void *user_data)
DBG("");
if (!extract_ss_info(result, &status))
return;
status = handle_ss_info(result, gprs);
ofono_gprs_status_notify(gprs, status);
if (status >= 0)
ofono_gprs_status_notify(gprs, status);
}
static void attach_detach_cb(struct qmi_result *result, void *user_data)
@@ -124,22 +158,26 @@ error:
static void get_ss_info_cb(struct qmi_result *result, void *user_data)
{
struct cb_data *cbd = user_data;
struct ofono_gprs *gprs = cbd->user;
ofono_gprs_status_cb_t cb = cbd->cb;
int status;
DBG("");
if (qmi_result_set_error(result, NULL)) {
CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
return;
}
if (qmi_result_set_error(result, NULL))
goto error;
if (!extract_ss_info(result, &status)) {
CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
return;
}
status = handle_ss_info(result, gprs);
if (status < 0)
goto error;
CALLBACK_WITH_SUCCESS(cb, status, cbd->data);
return;
error:
CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
}
static void qmi_attached_status(struct ofono_gprs *gprs,
@@ -150,6 +188,7 @@ static void qmi_attached_status(struct ofono_gprs *gprs,
DBG("");
cbd->user = gprs;
if (qmi_service_send(data->nas, QMI_NAS_GET_SS_INFO, NULL,
get_ss_info_cb, cbd, g_free) > 0)
return;
@@ -174,6 +213,13 @@ static void create_nas_cb(struct qmi_service *service, void *user_data)
data->nas = qmi_service_ref(service);
/*
* First get the SS info - the modem may already be connected,
* and the state-change notification may never arrive
*/
qmi_service_send(data->nas, QMI_NAS_GET_SS_INFO, NULL,
ss_info_notify, gprs, NULL);
qmi_service_register(data->nas, QMI_NAS_SS_INFO_IND,
ss_info_notify, gprs, NULL);
@@ -194,7 +240,8 @@ static int qmi_gprs_probe(struct ofono_gprs *gprs,
ofono_gprs_set_data(gprs, data);
qmi_service_create(device, QMI_SERVICE_NAS, create_nas_cb, gprs, NULL);
qmi_service_create_shared(device, QMI_SERVICE_NAS,
create_nas_cb, gprs, NULL);
return 0;
}

View File

@@ -0,0 +1,38 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 Jonas Bonn. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#include "nas.h"
#include "src/common.h"
int qmi_nas_rat_to_tech(uint8_t rat)
{
switch (rat) {
case QMI_NAS_NETWORK_RAT_GSM:
return ACCESS_TECHNOLOGY_GSM;
case QMI_NAS_NETWORK_RAT_UMTS:
return ACCESS_TECHNOLOGY_UTRAN;
case QMI_NAS_NETWORK_RAT_LTE:
return ACCESS_TECHNOLOGY_EUTRAN;
}
return -1;
}

View File

@@ -19,6 +19,8 @@
*
*/
#include <stdint.h>
#define QMI_NAS_RESET 0 /* Reset NAS service state variables */
#define QMI_NAS_ABORT 1 /* Abort previously issued NAS command */
#define QMI_NAS_EVENT 2 /* Connection state report indication */
@@ -63,7 +65,7 @@ struct qmi_nas_rf_info {
} __attribute__((__packed__));
/* Get the signal strength */
#define QMI_NAS_RESULT_SIGNAL_STRENGTH 0x10
#define QMI_NAS_RESULT_SIGNAL_STRENGTH 0x01
/* Scan for visible network */
#define QMI_NAS_PARAM_NETWORK_MASK 0x10 /* uint8 bitmask */
@@ -140,9 +142,17 @@ struct qmi_nas_current_plmn {
#define QMI_NAS_RESULT_LOCATION_AREA_CODE 0x1d /* uint16 */
#define QMI_NAS_RESULT_CELL_ID 0x1e /* uint32 */
#define QMI_NAS_ATTACH_STATUS_INVALID 0x00
#define QMI_NAS_ATTACH_STATUS_ATTACHED 0x01
#define QMI_NAS_ATTACH_STATUS_DETACHED 0x02
/* qmi_nas_serving_system.status */
#define QMI_NAS_REGISTRATION_STATE_NOT_REGISTERED 0x00
#define QMI_NAS_REGISTRATION_STATE_REGISTERED 0x01
#define QMI_NAS_REGISTRATION_STATE_SEARCHING 0x02
#define QMI_NAS_REGISTRATION_STATE_DENIED 0x03
#define QMI_NAS_REGISTRATION_STATE_UNKNOWN 0x04
/* cs_state/ps_state */
#define QMI_NAS_ATTACH_STATE_INVALID 0x00
#define QMI_NAS_ATTACH_STATE_ATTACHED 0x01
#define QMI_NAS_ATTACH_STATE_DETACHED 0x02
/* Get info about home network */
#define QMI_NAS_RESULT_HOME_NETWORK 0x01
@@ -152,3 +162,5 @@ struct qmi_nas_home_network {
uint8_t desc_len;
char desc[0];
} __attribute__((__packed__));
int qmi_nas_rat_to_tech(uint8_t rat);

View File

@@ -43,20 +43,6 @@ struct netreg_data {
uint8_t current_rat;
};
static int rat_to_tech(uint8_t rat)
{
switch (rat) {
case QMI_NAS_NETWORK_RAT_GSM:
return ACCESS_TECHNOLOGY_GSM;
case QMI_NAS_NETWORK_RAT_UMTS:
return ACCESS_TECHNOLOGY_UTRAN;
case QMI_NAS_NETWORK_RAT_LTE:
return ACCESS_TECHNOLOGY_EUTRAN;
}
return -1;
}
static bool extract_ss_info(struct qmi_result *result, int *status,
int *lac, int *cellid, int *tech,
struct ofono_network_operator *operator)
@@ -64,7 +50,7 @@ static bool extract_ss_info(struct qmi_result *result, int *status,
const struct qmi_nas_serving_system *ss;
const struct qmi_nas_current_plmn *plmn;
uint8_t i, roaming;
uint16_t value16, len;
uint16_t value16, len, opname_len;
uint32_t value32;
DBG("");
@@ -82,13 +68,13 @@ static bool extract_ss_info(struct qmi_result *result, int *status,
for (i = 0; i < ss->radio_if_count; i++) {
DBG("radio in use %d", ss->radio_if[i]);
*tech = rat_to_tech(ss->radio_if[i]);
*tech = qmi_nas_rat_to_tech(ss->radio_if[i]);
}
if (qmi_result_get_uint8(result, QMI_NAS_RESULT_ROAMING_STATUS,
&roaming)) {
if (ss->status == 1 && roaming == 0)
*status = 5;
*status = NETWORK_REGISTRATION_STATUS_ROAMING;
}
if (!operator)
@@ -100,8 +86,21 @@ static bool extract_ss_info(struct qmi_result *result, int *status,
GUINT16_FROM_LE(plmn->mcc));
snprintf(operator->mnc, OFONO_MAX_MNC_LENGTH + 1, "%02d",
GUINT16_FROM_LE(plmn->mnc));
strncpy(operator->name, plmn->desc, plmn->desc_len);
operator->name[plmn->desc_len] = '\0';
opname_len = plmn->desc_len;
if (opname_len > OFONO_MAX_OPERATOR_NAME_LENGTH)
opname_len = OFONO_MAX_OPERATOR_NAME_LENGTH;
/*
* Telit QMI modems can return non-utf-8 characters in
* plmn-desc. When that happens, libdbus will abort ofono.
* If non-utf-8 characters are detected, use mccmnc string.
*/
if (g_utf8_validate(plmn->desc, opname_len, NULL)) {
strncpy(operator->name, plmn->desc, opname_len);
operator->name[opname_len] = '\0';
} else
snprintf(operator->name, OFONO_MAX_OPERATOR_NAME_LENGTH,
"%s%s", operator->mcc, operator->mnc);
DBG("%s (%s:%s)", operator->name, operator->mcc, operator->mnc);
}
@@ -265,7 +264,7 @@ static void scan_nets_cb(struct qmi_result *result, void *user_data)
DBG("%03d:%02d %d", netrat->info[i].mcc, netrat->info[i].mnc,
netrat->info[i].rat);
list[i].tech = rat_to_tech(netrat->info[i].rat);
list[i].tech = qmi_nas_rat_to_tech(netrat->info[i].rat);
}
done:
@@ -543,7 +542,7 @@ static int qmi_netreg_probe(struct ofono_netreg *netreg,
ofono_netreg_set_data(netreg, data);
qmi_service_create(device, QMI_SERVICE_NAS,
qmi_service_create_shared(device, QMI_SERVICE_NAS,
create_nas_cb, netreg, NULL);
return 0;

View File

@@ -26,6 +26,8 @@
#define _GNU_SOURCE
#include <stdio.h>
#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
@@ -33,12 +35,18 @@
#include <glib.h>
#include <ofono/log.h>
#include "qmi.h"
#include "ctl.h"
typedef void (*qmi_message_func_t)(uint16_t message, uint16_t length,
const void *buffer, void *user_data);
struct discovery {
qmi_destroy_func_t destroy;
};
struct qmi_device {
int ref_count;
int fd;
@@ -49,6 +57,7 @@ struct qmi_device {
GQueue *req_queue;
GQueue *control_queue;
GQueue *service_queue;
GQueue *discovery_queue;
uint8_t next_control_tid;
uint16_t next_service_tid;
qmi_debug_func_t debug_func;
@@ -60,6 +69,10 @@ struct qmi_device {
uint8_t version_count;
GHashTable *service_list;
unsigned int release_users;
qmi_shutdown_func_t shutdown_func;
void *shutdown_user_data;
qmi_destroy_func_t shutdown_destroy;
guint shutdown_source;
};
struct qmi_service {
@@ -209,6 +222,14 @@ static gint __request_compare(gconstpointer a, gconstpointer b)
return req->tid - tid;
}
static void __discovery_free(gpointer data, gpointer user_data)
{
struct discovery *d = data;
qmi_destroy_func_t destroy = d->destroy;
destroy(d);
}
static void __notify_free(gpointer data, gpointer user_data)
{
struct qmi_notify *notify = data;
@@ -313,8 +334,12 @@ static const char *__service_type_to_string(uint8_t type)
return "UIM";
case QMI_SERVICE_PBM:
return "PBM";
case QMI_SERVICE_QCHAT:
return "QCHAT";
case QMI_SERVICE_RMTFS:
return "RMTFS";
case QMI_SERVICE_TEST:
return "TEST";
case QMI_SERVICE_LOC:
return "LOC";
case QMI_SERVICE_SAR:
@@ -326,9 +351,21 @@ static const char *__service_type_to_string(uint8_t type)
case QMI_SERVICE_TS:
return "TS";
case QMI_SERVICE_TMD:
return "TMS";
return "TMD";
case QMI_SERVICE_WDA:
return "WDA";
case QMI_SERVICE_CSVT:
return "CSVT";
case QMI_SERVICE_COEX:
return "COEX";
case QMI_SERVICE_PDC:
return "PDC";
case QMI_SERVICE_RFRPE:
return "RFRPE";
case QMI_SERVICE_DSD:
return "DSD";
case QMI_SERVICE_SSCTL:
return "SSCTL";
case QMI_SERVICE_CAT_OLD:
return "CAT";
case QMI_SERVICE_RMS:
@@ -758,7 +795,7 @@ static void handle_packet(struct qmi_device *device,
tid = GUINT16_FROM_LE(service->transaction);
if (service->type == 0x04 && tid == 0x0000) {
if (service->type == 0x04) {
handle_indication(device, hdr->service, hdr->client,
message, length, data);
return;
@@ -838,6 +875,21 @@ static void read_watch_destroy(gpointer user_data)
device->read_watch = 0;
}
static void __qmi_device_discovery_started(struct qmi_device *device,
struct discovery *d)
{
g_queue_push_tail(device->discovery_queue, d);
}
static void __qmi_device_discovery_complete(struct qmi_device *device,
struct discovery *d)
{
if (g_queue_remove(device->discovery_queue, d) != TRUE)
return;
__discovery_free(d, NULL);
}
static void service_destroy(gpointer data)
{
struct qmi_service *service = data;
@@ -891,6 +943,7 @@ struct qmi_device *qmi_device_new(int fd)
device->req_queue = g_queue_new();
device->control_queue = g_queue_new();
device->service_queue = g_queue_new();
device->discovery_queue = g_queue_new();
device->service_list = g_hash_table_new_full(g_direct_hash,
g_direct_equal, NULL, service_destroy);
@@ -927,6 +980,9 @@ void qmi_device_unref(struct qmi_device *device)
g_queue_foreach(device->req_queue, __request_free, NULL);
g_queue_free(device->req_queue);
g_queue_foreach(device->discovery_queue, __discovery_free, NULL);
g_queue_free(device->discovery_queue);
if (device->write_watch > 0)
g_source_remove(device->write_watch);
@@ -936,6 +992,9 @@ void qmi_device_unref(struct qmi_device *device)
if (device->close_on_unref)
close(device->fd);
if (device->shutdown_source)
g_source_remove(device->shutdown_source);
g_hash_table_destroy(device->service_list);
g_free(device->version_str);
@@ -987,6 +1046,7 @@ static const void *tlv_get(const void *data, uint16_t size,
}
struct discover_data {
struct discovery super;
struct qmi_device *device;
qmi_discover_func_t func;
void *user_data;
@@ -994,6 +1054,21 @@ struct discover_data {
guint timeout;
};
static void discover_data_free(gpointer user_data)
{
struct discover_data *data = user_data;
if (data->timeout) {
g_source_remove(data->timeout);
data->timeout = 0;
}
if (data->destroy)
data->destroy(data->user_data);
g_free(data);
}
static void discover_callback(uint16_t message, uint16_t length,
const void *buffer, void *user_data)
{
@@ -1007,8 +1082,6 @@ static void discover_callback(uint16_t message, uint16_t length,
uint8_t count;
unsigned int i;
g_source_remove(data->timeout);
count = 0;
list = NULL;
@@ -1079,10 +1152,7 @@ done:
if (data->func)
data->func(count, list, data->user_data);
if (data->destroy)
data->destroy(data->user_data);
g_free(data);
__qmi_device_discovery_complete(data->device, &data->super);
}
static gboolean discover_reply(gpointer user_data)
@@ -1096,10 +1166,7 @@ static gboolean discover_reply(gpointer user_data)
data->func(device->version_count,
device->version_list, data->user_data);
if (data->destroy)
data->destroy(data->user_data);
g_free(data);
__qmi_device_discovery_complete(data->device, &data->super);
return FALSE;
}
@@ -1120,13 +1187,15 @@ bool qmi_device_discover(struct qmi_device *device, qmi_discover_func_t func,
if (!data)
return false;
data->super.destroy = discover_data_free;
data->device = device;
data->func = func;
data->user_data = user_data;
data->destroy = destroy;
if (device->version_list) {
g_timeout_add_seconds(0, discover_reply, data);
data->timeout = g_timeout_add_seconds(0, discover_reply, data);
__qmi_device_discovery_started(device, &data->super);
return true;
}
@@ -1147,6 +1216,7 @@ bool qmi_device_discover(struct qmi_device *device, qmi_discover_func_t func,
__request_submit(device, req, hdr->transaction);
data->timeout = g_timeout_add_seconds(5, discover_reply, data);
__qmi_device_discovery_started(device, &data->super);
return true;
}
@@ -1177,63 +1247,249 @@ static void release_client(struct qmi_device *device,
__request_submit(device, req, hdr->transaction);
}
struct shutdown_data {
struct qmi_device *device;
qmi_shutdown_func_t func;
void *user_data;
qmi_destroy_func_t destroy;
};
static gboolean shutdown_reply(gpointer user_data)
static void shutdown_destroy(gpointer user_data)
{
struct shutdown_data *data = user_data;
struct qmi_device *device = user_data;
if (data->func)
data->func(data->user_data);
if (device->shutdown_destroy)
device->shutdown_destroy(device->shutdown_user_data);
g_free(data);
return FALSE;
device->shutdown_source = 0;
}
static gboolean shutdown_timeout(gpointer user_data)
static gboolean shutdown_callback(gpointer user_data)
{
struct shutdown_data *data = user_data;
struct qmi_device *device = data->device;
struct qmi_device *device = user_data;
if (device->release_users > 0)
return TRUE;
return shutdown_reply(data);
if (device->shutdown_func)
device->shutdown_func(device->shutdown_user_data);
return FALSE;
}
bool qmi_device_shutdown(struct qmi_device *device, qmi_shutdown_func_t func,
void *user_data, qmi_destroy_func_t destroy)
{
struct shutdown_data *data;
if (!device)
return false;
if (device->shutdown_source > 0)
return false;
__debug_device(device, "device %p shutdown", device);
data = g_try_new0(struct shutdown_data, 1);
if (!data)
device->shutdown_source = g_timeout_add_seconds_full(G_PRIORITY_DEFAULT,
0, shutdown_callback, device,
shutdown_destroy);
if (device->shutdown_source == 0)
return false;
data->device = device;
data->func = func;
data->user_data = user_data;
data->destroy = destroy;
if (device->release_users > 0)
g_timeout_add_seconds(0, shutdown_timeout, data);
else
g_timeout_add_seconds(0, shutdown_reply, data);
device->shutdown_func = func;
device->shutdown_user_data = user_data;
device->shutdown_destroy = destroy;
return true;
}
static bool get_device_file_name(struct qmi_device *device,
char *file_name, int size)
{
pid_t pid;
char temp[100];
ssize_t result;
if (size <= 0)
return false;
pid = getpid();
snprintf(temp, 100, "/proc/%d/fd/%d", (int) pid, device->fd);
temp[99] = 0;
result = readlink(temp, file_name, size - 1);
if (result == -1 || result >= size - 1) {
DBG("Error %d in readlink", errno);
return false;
}
file_name[result] = 0;
return true;
}
static char *get_first_dir_in_directory(char *dir_path)
{
DIR *dir;
struct dirent *dir_entry;
char *dir_name = NULL;
dir = opendir(dir_path);
if (!dir)
return NULL;
dir_entry = readdir(dir);
while ((dir_entry != NULL)) {
if (dir_entry->d_type == DT_DIR &&
strcmp(dir_entry->d_name, ".") != 0 &&
strcmp(dir_entry->d_name, "..") != 0) {
dir_name = g_strdup(dir_entry->d_name);
break;
}
dir_entry = readdir(dir);
}
closedir(dir);
return dir_name;
}
static char *get_device_interface(struct qmi_device *device)
{
char * const driver_names[] = { "usbmisc", "usb" };
unsigned int i;
char file_path[PATH_MAX];
char *file_name;
char *interface = NULL;
if (!get_device_file_name(device, file_path, sizeof(file_path)))
return NULL;
file_name = basename(file_path);
for (i = 0; i < G_N_ELEMENTS(driver_names) && !interface; i++) {
gchar *sysfs_path;
sysfs_path = g_strdup_printf("/sys/class/%s/%s/device/net/",
driver_names[i], file_name);
interface = get_first_dir_in_directory(sysfs_path);
g_free(sysfs_path);
}
return interface;
}
enum qmi_device_expected_data_format qmi_device_get_expected_data_format(
struct qmi_device *device)
{
char *sysfs_path = NULL;
char *interface = NULL;
int fd = -1;
char value;
enum qmi_device_expected_data_format expected =
QMI_DEVICE_EXPECTED_DATA_FORMAT_UNKNOWN;
if (!device)
goto done;
interface = get_device_interface(device);
if (!interface) {
DBG("Error while getting interface name");
goto done;
}
/* Build sysfs file path and open it */
sysfs_path = g_strdup_printf("/sys/class/net/%s/qmi/raw_ip", interface);
fd = open(sysfs_path, O_RDONLY);
if (fd < 0) {
/* maybe not supported by kernel */
DBG("Error %d in open(%s)", errno, sysfs_path);
goto done;
}
if (read(fd, &value, 1) != 1) {
DBG("Error %d in read(%s)", errno, sysfs_path);
goto done;
}
if (value == 'Y')
expected = QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP;
else if (value == 'N')
expected = QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3;
else
DBG("Unexpected sysfs file contents");
done:
if (fd >= 0)
close(fd);
if (sysfs_path)
g_free(sysfs_path);
if (interface)
g_free(interface);
return expected;
}
bool qmi_device_set_expected_data_format(struct qmi_device *device,
enum qmi_device_expected_data_format format)
{
bool res = false;
char *sysfs_path = NULL;
char *interface = NULL;
int fd = -1;
char value;
if (!device)
goto done;
switch (format) {
case QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3:
value = 'N';
break;
case QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP:
value = 'Y';
break;
default:
DBG("Unhandled format: %d", (int) format);
goto done;
}
interface = get_device_interface(device);
if (!interface) {
DBG("Error while getting interface name");
goto done;
}
/* Build sysfs file path and open it */
sysfs_path = g_strdup_printf("/sys/class/net/%s/qmi/raw_ip", interface);
fd = open(sysfs_path, O_WRONLY);
if (fd < 0) {
/* maybe not supported by kernel */
DBG("Error %d in open(%s)", errno, sysfs_path);
goto done;
}
if (write(fd, &value, 1) != 1) {
DBG("Error %d in write(%s)", errno, sysfs_path);
goto done;
}
res = true;
done:
if (fd >= 0)
close(fd);
if (sysfs_path)
g_free(sysfs_path);
if (interface)
g_free(interface);
return res;
}
struct qmi_param *qmi_param_new(void)
{
struct qmi_param *param;
@@ -1501,6 +1757,7 @@ bool qmi_result_get_uint64(struct qmi_result *result, uint8_t type,
}
struct service_create_data {
struct discovery super;
struct qmi_device *device;
bool shared;
uint8_t type;
@@ -1512,16 +1769,29 @@ struct service_create_data {
guint timeout;
};
static gboolean service_create_reply(gpointer user_data)
static void service_create_data_free(gpointer user_data)
{
struct service_create_data *data = user_data;
data->func(NULL, data->user_data);
if (data->timeout) {
g_source_remove(data->timeout);
data->timeout = 0;
}
if (data->destroy)
data->destroy(data->user_data);
g_free(data);
}
static gboolean service_create_reply(gpointer user_data)
{
struct service_create_data *data = user_data;
data->timeout = 0;
data->func(NULL, data->user_data);
__qmi_device_discovery_complete(data->device, &data->super);
return FALSE;
}
@@ -1537,8 +1807,6 @@ static void service_create_callback(uint16_t message, uint16_t length,
uint16_t len;
unsigned int hash_id;
g_source_remove(data->timeout);
result_code = tlv_get(buffer, length, 0x02, &len);
if (!result_code)
goto done;
@@ -1580,13 +1848,9 @@ static void service_create_callback(uint16_t message, uint16_t length,
done:
data->func(service, data->user_data);
qmi_service_unref(service);
if (data->destroy)
data->destroy(data->user_data);
g_free(data);
__qmi_device_discovery_complete(data->device, &data->super);
}
static void service_create_discover(uint8_t count,
@@ -1617,7 +1881,9 @@ static void service_create_discover(uint8_t count,
if (data->timeout > 0)
g_source_remove(data->timeout);
g_timeout_add_seconds(0, service_create_reply, data);
data->timeout = g_timeout_add_seconds(0,
service_create_reply, data);
__qmi_device_discovery_started(device, &data->super);
return;
}
@@ -1640,6 +1906,7 @@ static bool service_create(struct qmi_device *device, bool shared,
if (!data)
return false;
data->super.destroy = service_create_data_free;
data->device = device;
data->shared = shared;
data->type = type;
@@ -1662,6 +1929,7 @@ static bool service_create(struct qmi_device *device, bool shared,
done:
data->timeout = g_timeout_add_seconds(8, service_create_reply, data);
__qmi_device_discovery_started(device, &data->super);
return true;
}
@@ -1680,17 +1948,23 @@ bool qmi_service_create(struct qmi_device *device,
}
struct service_create_shared_data {
struct discovery super;
struct qmi_service *service;
struct qmi_device *device;
qmi_create_func_t func;
void *user_data;
qmi_destroy_func_t destroy;
guint timeout;
};
static gboolean service_create_shared_reply(gpointer user_data)
static void service_create_shared_data_free(gpointer user_data)
{
struct service_create_shared_data *data = user_data;
data->func(data->service, data->user_data);
if (data->timeout) {
g_source_remove(data->timeout);
data->timeout = 0;
}
qmi_service_unref(data->service);
@@ -1698,6 +1972,16 @@ static gboolean service_create_shared_reply(gpointer user_data)
data->destroy(data->user_data);
g_free(data);
}
static gboolean service_create_shared_reply(gpointer user_data)
{
struct service_create_shared_data *data = user_data;
data->timeout = 0;
data->func(data->service, data->user_data);
__qmi_device_discovery_complete(data->device, &data->super);
return FALSE;
}
@@ -1724,13 +2008,16 @@ bool qmi_service_create_shared(struct qmi_device *device,
if (!data)
return false;
data->super.destroy = service_create_shared_data_free;
data->service = qmi_service_ref(service);
data->device = device;
data->func = func;
data->user_data = user_data;
data->destroy = destroy;
g_timeout_add(0, service_create_shared_reply, data);
data->timeout = g_timeout_add(0,
service_create_shared_reply, data);
__qmi_device_discovery_started(device, &data->super);
return 0;
}

View File

@@ -35,18 +35,32 @@
#define QMI_SERVICE_CAT 10 /* Card application toolkit service */
#define QMI_SERVICE_UIM 11 /* UIM service */
#define QMI_SERVICE_PBM 12 /* Phonebook service */
#define QMI_SERVICE_QCHAT 13
#define QMI_SERVICE_RMTFS 14 /* Remote file system service */
#define QMI_SERVICE_TEST 15
#define QMI_SERVICE_LOC 16 /* Location service */
#define QMI_SERVICE_SAR 17 /* Specific absorption rate service */
#define QMI_SERVICE_CSD 20 /* Core sound driver service */
#define QMI_SERVICE_EFS 21 /* Embedded file system service */
#define QMI_SERVICE_TS 23 /* Thermal sensors service */
#define QMI_SERVICE_TMD 24 /* Thermal mitigation device service */
#define QMI_SERVICE_WDA 26 /* Wireless data administrative service */
#define QMI_SERVICE_CSVT 29
#define QMI_SERVICE_COEX 34
#define QMI_SERVICE_PDC 36 /* Persistent device configuration service */
#define QMI_SERVICE_RFRPE 41
#define QMI_SERVICE_DSD 42
#define QMI_SERVICE_SSCTL 43
#define QMI_SERVICE_CAT_OLD 224 /* Card application toolkit service */
#define QMI_SERVICE_RMS 225 /* Remote management service */
#define QMI_SERVICE_OMA 226 /* OMA device management service */
enum qmi_device_expected_data_format {
QMI_DEVICE_EXPECTED_DATA_FORMAT_UNKNOWN,
QMI_DEVICE_EXPECTED_DATA_FORMAT_802_3,
QMI_DEVICE_EXPECTED_DATA_FORMAT_RAW_IP,
};
struct qmi_version {
uint8_t type;
uint16_t major;
@@ -82,6 +96,10 @@ bool qmi_device_discover(struct qmi_device *device, qmi_discover_func_t func,
bool qmi_device_shutdown(struct qmi_device *device, qmi_shutdown_func_t func,
void *user_data, qmi_destroy_func_t destroy);
enum qmi_device_expected_data_format qmi_device_get_expected_data_format(
struct qmi_device *device);
bool qmi_device_set_expected_data_format(struct qmi_device *device,
enum qmi_device_expected_data_format format);
struct qmi_param;

View File

@@ -74,7 +74,8 @@ static int qmi_radio_settings_probe(struct ofono_radio_settings *rs,
ofono_radio_settings_set_data(rs, data);
qmi_service_create(device, QMI_SERVICE_NAS, create_nas_cb, rs, NULL);
qmi_service_create_shared(device, QMI_SERVICE_NAS,
create_nas_cb, rs, NULL);
return 0;

View File

@@ -30,6 +30,7 @@
#include <ofono/sim.h>
#include "qmi.h"
#include "dms.h"
#include "uim.h"
#include "qmimodem.h"
@@ -38,15 +39,36 @@
#define EF_STATUS_INVALIDATED 0
#define EF_STATUS_VALID 1
struct sim_data {
struct qmi_service *uim;
uint32_t event_mask;
/* max number of retry of commands that can temporary fail */
#define MAX_RETRY_COUNT 100
enum get_card_status_result {
GET_CARD_STATUS_RESULT_OK, /* No error */
GET_CARD_STATUS_RESULT_ERROR, /* Definitive error */
GET_CARD_STATUS_RESULT_TEMP_ERROR, /* error, a retry could work */
};
/* information from QMI_UIM_GET_CARD_STATUS command */
struct sim_status {
uint8_t card_state;
uint8_t app_type;
uint8_t passwd_state;
int retries[OFONO_SIM_PASSWORD_INVALID];
};
struct sim_data {
struct qmi_device *qmi_dev;
struct qmi_service *dms;
struct qmi_service *uim;
uint32_t event_mask;
uint8_t app_type;
uint32_t retry_count;
guint poll_source;
};
static void qmi_query_passwd_state(struct ofono_sim *sim,
ofono_sim_passwd_cb_t cb, void *user_data);
static int create_fileid_data(uint8_t app_type, int fileid,
const unsigned char *path,
unsigned int path_len,
@@ -146,7 +168,7 @@ static void qmi_read_attributes(struct ofono_sim *sim, int fileid,
{
struct sim_data *data = ofono_sim_get_data(sim);
struct cb_data *cbd = cb_data_new(cb, user_data);
unsigned char aid_data[2] = { 0x06, 0x00 };
unsigned char aid_data[2] = { 0x00, 0x00 };
unsigned char fileid_data[9];
int fileid_len;
struct qmi_param *param;
@@ -211,7 +233,7 @@ static void qmi_read_transparent(struct ofono_sim *sim,
{
struct sim_data *data = ofono_sim_get_data(sim);
struct cb_data *cbd = cb_data_new(cb, user_data);
unsigned char aid_data[2] = { 0x06, 0x00 };
unsigned char aid_data[2] = { 0x00, 0x00 };
unsigned char read_data[4];
unsigned char fileid_data[9];
int fileid_len;
@@ -257,7 +279,7 @@ static void qmi_read_record(struct ofono_sim *sim,
{
struct sim_data *data = ofono_sim_get_data(sim);
struct cb_data *cbd = cb_data_new(cb, user_data);
unsigned char aid_data[2] = { 0x06, 0x00 };
unsigned char aid_data[2] = { 0x00, 0x00 };
unsigned char read_data[4];
unsigned char fileid_data[9];
int fileid_len;
@@ -295,76 +317,96 @@ error:
g_free(cbd);
}
static void qmi_query_passwd_state(struct ofono_sim *sim,
ofono_sim_passwd_cb_t cb, void *user_data)
static void get_imsi_cb(struct qmi_result *result, void *user_data)
{
struct sim_data *data = ofono_sim_get_data(sim);
struct cb_data *cbd = user_data;
ofono_sim_imsi_cb_t cb = cbd->cb;
char *str;
DBG("passwd state %d", data->passwd_state);
DBG("");
if (data->passwd_state == OFONO_SIM_PASSWORD_INVALID) {
CALLBACK_WITH_FAILURE(cb, -1, user_data);
if (qmi_result_set_error(result, NULL)) {
CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
return;
}
CALLBACK_WITH_SUCCESS(cb, data->passwd_state, user_data);
}
static void qmi_query_pin_retries(struct ofono_sim *sim,
ofono_sim_pin_retries_cb_t cb, void *user_data)
{
struct sim_data *data = ofono_sim_get_data(sim);
DBG("passwd state %d", data->passwd_state);
if (data->passwd_state == OFONO_SIM_PASSWORD_INVALID) {
CALLBACK_WITH_FAILURE(cb, NULL, user_data);
str = qmi_result_get_string(result, QMI_DMS_RESULT_IMSI);
if (!str) {
CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
return;
}
CALLBACK_WITH_SUCCESS(cb, data->retries, user_data);
CALLBACK_WITH_SUCCESS(cb, str, cbd->data);
qmi_free(str);
}
static void card_setup(const struct qmi_uim_slot_info *slot,
static void qmi_read_imsi(struct ofono_sim *sim,
ofono_sim_imsi_cb_t cb, void *user_data)
{
struct sim_data *data = ofono_sim_get_data(sim);
struct cb_data *cbd = cb_data_new(cb, user_data);
DBG("");
if (qmi_service_send(data->dms, QMI_DMS_GET_IMSI, NULL,
get_imsi_cb, cbd, g_free) > 0)
return;
CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
g_free(cbd);
}
/* Return true if a retry could give another (better) result */
static bool get_card_status(const struct qmi_uim_slot_info *slot,
const struct qmi_uim_app_info1 *info1,
const struct qmi_uim_app_info2 *info2,
struct sim_data *data)
struct sim_status *sim_stat)
{
data->card_state = slot->card_state;
data->app_type = info1->app_type;
bool need_retry = false;
sim_stat->card_state = slot->card_state;
sim_stat->app_type = info1->app_type;
switch (info1->app_state) {
case 0x02: /* PIN1 or UPIN is required */
data->passwd_state = OFONO_SIM_PASSWORD_SIM_PIN;
sim_stat->passwd_state = OFONO_SIM_PASSWORD_SIM_PIN;
break;
case 0x03: /* PUK1 or PUK for UPIN is required */
data->passwd_state = OFONO_SIM_PASSWORD_SIM_PUK;
sim_stat->passwd_state = OFONO_SIM_PASSWORD_SIM_PUK;
break;
case 0x04: /* Personalization state must be checked. */
/* This is temporary, we could retry and get another result */
sim_stat->passwd_state = OFONO_SIM_PASSWORD_INVALID;
need_retry = true;
break;
case 0x07: /* Ready */
data->passwd_state = OFONO_SIM_PASSWORD_NONE;
sim_stat->passwd_state = OFONO_SIM_PASSWORD_NONE;
break;
default:
data->passwd_state = OFONO_SIM_PASSWORD_INVALID;
DBG("info1->app_state:0x%x: OFONO_SIM_PASSWORD_INVALID",
info1->app_state);
sim_stat->passwd_state = OFONO_SIM_PASSWORD_INVALID;
break;
}
data->retries[OFONO_SIM_PASSWORD_SIM_PIN] = info2->pin1_retries;
data->retries[OFONO_SIM_PASSWORD_SIM_PUK] = info2->puk1_retries;
sim_stat->retries[OFONO_SIM_PASSWORD_SIM_PIN] = info2->pin1_retries;
sim_stat->retries[OFONO_SIM_PASSWORD_SIM_PUK] = info2->puk1_retries;
data->retries[OFONO_SIM_PASSWORD_SIM_PIN2] = info2->pin2_retries;
data->retries[OFONO_SIM_PASSWORD_SIM_PUK2] = info2->puk2_retries;
sim_stat->retries[OFONO_SIM_PASSWORD_SIM_PIN2] = info2->pin2_retries;
sim_stat->retries[OFONO_SIM_PASSWORD_SIM_PUK2] = info2->puk2_retries;
return need_retry;
}
static void get_card_status_cb(struct qmi_result *result, void *user_data)
static enum get_card_status_result handle_get_card_status_result(
struct qmi_result *result, struct sim_status *sim_stat)
{
struct ofono_sim *sim = user_data;
struct sim_data *data = ofono_sim_get_data(sim);
const void *ptr;
const struct qmi_uim_card_status *status;
uint16_t len, offset;
uint8_t i;
DBG("");
enum get_card_status_result res = GET_CARD_STATUS_RESULT_ERROR;
if (qmi_result_set_error(result, NULL))
goto done;
@@ -397,15 +439,211 @@ static void get_card_status_cb(struct qmi_result *result, void *user_data)
index = GUINT16_FROM_LE(status->index_gw_pri);
if ((index & 0xff) == i && (index >> 8) == n)
card_setup(slot, info1, info2, data);
if ((index & 0xff) == i && (index >> 8) == n) {
if (get_card_status(slot, info1, info2,
sim_stat))
res = GET_CARD_STATUS_RESULT_TEMP_ERROR;
else
res = GET_CARD_STATUS_RESULT_OK;
}
}
}
done:
return res;
}
static gboolean query_passwd_state_retry(gpointer userdata)
{
struct cb_data *cbd = userdata;
ofono_sim_passwd_cb_t cb = cbd->cb;
struct ofono_sim *sim = cbd->user;
struct sim_data *data = ofono_sim_get_data(sim);
data->poll_source = 0;
qmi_query_passwd_state(sim, cb, cbd->data);
return FALSE;
}
static void query_passwd_state_cb(struct qmi_result *result,
void *user_data)
{
struct cb_data *cbd = user_data;
ofono_sim_passwd_cb_t cb = cbd->cb;
struct ofono_sim *sim = cbd->user;
struct sim_data *data = ofono_sim_get_data(sim);
struct sim_status sim_stat;
enum get_card_status_result res;
struct cb_data *retry_cbd;
res = handle_get_card_status_result(result, &sim_stat);
switch (res) {
case GET_CARD_STATUS_RESULT_OK:
DBG("passwd state %d", sim_stat.passwd_state);
data->retry_count = 0;
CALLBACK_WITH_SUCCESS(cb, sim_stat.passwd_state, cbd->data);
break;
case GET_CARD_STATUS_RESULT_TEMP_ERROR:
data->retry_count++;
if (data->retry_count > MAX_RETRY_COUNT) {
DBG("Failed after %d attempts", data->retry_count);
data->retry_count = 0;
CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
} else {
DBG("Retry command");
retry_cbd = cb_data_new(cb, cbd->data);
retry_cbd->user = sim;
data->poll_source = g_timeout_add(20,
query_passwd_state_retry,
retry_cbd);
}
break;
case GET_CARD_STATUS_RESULT_ERROR:
DBG("Command failed");
data->retry_count = 0;
CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
break;
}
}
static void qmi_query_passwd_state(struct ofono_sim *sim,
ofono_sim_passwd_cb_t cb, void *user_data)
{
struct sim_data *data = ofono_sim_get_data(sim);
struct cb_data *cbd = cb_data_new(cb, user_data);
DBG("");
cbd->user = sim;
if (qmi_service_send(data->uim, QMI_UIM_GET_CARD_STATUS, NULL,
query_passwd_state_cb, cbd, g_free) > 0)
return;
CALLBACK_WITH_FAILURE(cb, -1, cbd->data);
g_free(cbd);
}
static void query_pin_retries_cb(struct qmi_result *result, void *user_data)
{
struct cb_data *cbd = user_data;
ofono_sim_pin_retries_cb_t cb = cbd->cb;
struct sim_status sim_stat;
DBG("");
if (handle_get_card_status_result(result, &sim_stat) !=
GET_CARD_STATUS_RESULT_OK) {
CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
return;
}
CALLBACK_WITH_SUCCESS(cb, sim_stat.retries, cbd->data);
}
static void qmi_query_pin_retries(struct ofono_sim *sim,
ofono_sim_pin_retries_cb_t cb, void *user_data)
{
struct sim_data *data = ofono_sim_get_data(sim);
struct cb_data *cbd = cb_data_new(cb, user_data);
DBG("");
if (qmi_service_send(data->uim, QMI_UIM_GET_CARD_STATUS, NULL,
query_pin_retries_cb, cbd, g_free) > 0)
return;
CALLBACK_WITH_FAILURE(cb, NULL, cbd->data);
g_free(cbd);
}
static void pin_send_cb(struct qmi_result *result, void *user_data)
{
struct cb_data *cbd = user_data;
ofono_sim_lock_unlock_cb_t cb = cbd->cb;
DBG("");
if (qmi_result_set_error(result, NULL)) {
CALLBACK_WITH_FAILURE(cb, cbd->data);
return;
}
CALLBACK_WITH_SUCCESS(cb, cbd->data);
}
static void qmi_pin_send(struct ofono_sim *sim, const char *passwd,
ofono_sim_lock_unlock_cb_t cb, void *user_data)
{
struct sim_data *data = ofono_sim_get_data(sim);
struct cb_data *cbd = cb_data_new(cb, user_data);
int passwd_len;
struct qmi_param *param;
struct qmi_uim_param_message_info *info_data;
unsigned char session_info_data[2];
DBG("");
if (!passwd)
goto error;
passwd_len = strlen(passwd);
if (passwd_len <= 0 || passwd_len > 0xFF)
goto error;
param = qmi_param_new();
if (!param)
goto error;
/* param info */
info_data = alloca(2 + passwd_len);
info_data->pin_id = 0x01; /* PIN 1 */
info_data->length = (uint8_t) passwd_len;
memcpy(info_data->pin_value, passwd, passwd_len);
qmi_param_append(param, QMI_UIM_PARAM_MESSAGE_INFO, 2 + passwd_len,
info_data);
/* param Session Information */
session_info_data[0] = 0x6;
session_info_data[1] = 0x0;
qmi_param_append(param, QMI_UIM_PARAM_MESSAGE_SESSION_INFO, 2,
session_info_data);
if (qmi_service_send(data->uim, QMI_UIM_VERIFY_PIN, param,
pin_send_cb, cbd, g_free) > 0)
return;
qmi_param_free(param);
error:
CALLBACK_WITH_FAILURE(cb, cbd->data);
g_free(cbd);
}
static void get_card_status_cb(struct qmi_result *result, void *user_data)
{
struct ofono_sim *sim = user_data;
struct sim_data *data = ofono_sim_get_data(sim);
struct sim_status sim_stat;
DBG("");
if (handle_get_card_status_result(result, &sim_stat) !=
GET_CARD_STATUS_RESULT_OK) {
data->app_type = 0; /* Unknown */
sim_stat.card_state = 0x00; /* Absent */
} else {
data->app_type = sim_stat.app_type;
}
ofono_sim_register(sim);
switch (data->card_state) {
switch (sim_stat.card_state) {
case 0x00: /* Absent */
case 0x02: /* Error */
break;
@@ -465,30 +703,44 @@ static void create_uim_cb(struct qmi_service *service, void *user_data)
return;
error:
qmi_service_unref(data->uim);
ofono_sim_remove(sim);
}
static void create_dms_cb(struct qmi_service *service, void *user_data)
{
struct ofono_sim *sim = user_data;
struct sim_data *data = ofono_sim_get_data(sim);
DBG("");
if (!service) {
ofono_error("Failed to request DMS service");
ofono_sim_remove(sim);
return;
}
data->dms = qmi_service_ref(service);
qmi_service_create(data->qmi_dev, QMI_SERVICE_UIM, create_uim_cb, sim,
NULL);
}
static int qmi_sim_probe(struct ofono_sim *sim,
unsigned int vendor, void *user_data)
{
struct qmi_device *device = user_data;
struct sim_data *data;
int i;
DBG("");
data = g_new0(struct sim_data, 1);
data->passwd_state = OFONO_SIM_PASSWORD_INVALID;
for (i = 0; i < OFONO_SIM_PASSWORD_INVALID; i++)
data->retries[i] = -1;
data->qmi_dev = device;
ofono_sim_set_data(sim, data);
qmi_service_create(device, QMI_SERVICE_UIM, create_uim_cb, sim, NULL);
qmi_service_create_shared(device, QMI_SERVICE_DMS,
create_dms_cb, sim, NULL);
return 0;
}
@@ -501,9 +753,18 @@ static void qmi_sim_remove(struct ofono_sim *sim)
ofono_sim_set_data(sim, NULL);
qmi_service_unregister_all(data->uim);
if (data->poll_source > 0)
g_source_remove(data->poll_source);
qmi_service_unref(data->uim);
if (data->uim) {
qmi_service_unregister_all(data->uim);
qmi_service_unref(data->uim);
data->uim = NULL;
}
if (data->dms) {
qmi_service_unregister_all(data->dms);
qmi_service_unref(data->dms);
}
g_free(data);
}
@@ -516,8 +777,10 @@ static struct ofono_sim_driver driver = {
.read_file_transparent = qmi_read_transparent,
.read_file_linear = qmi_read_record,
.read_file_cyclic = qmi_read_record,
.read_imsi = qmi_read_imsi,
.query_passwd_state = qmi_query_passwd_state,
.query_pin_retries = qmi_query_pin_retries,
.send_passwd = qmi_pin_send,
};
void qmi_sim_init(void)

View File

@@ -25,6 +25,8 @@
#define QMI_UIM_WRITE_RECORD 35 /* Write a record */
#define QMI_UIM_GET_FILE_ATTRIBUTES 36 /* Get file attributes */
#define QMI_UIM_VERIFY_PIN 38 /* Verify PIN */
#define QMI_UIM_EVENT_REGISTRATION 46 /* Register for indications */
#define QMI_UIM_GET_CARD_STATUS 47 /* Get card status */
@@ -91,3 +93,12 @@ struct qmi_uim_file_attributes {
uint16_t raw_len;
uint8_t raw_value[0];
} __attribute__((__packed__));
/* Verify PIN parameter */
#define QMI_UIM_PARAM_MESSAGE_SESSION_INFO 0x01
#define QMI_UIM_PARAM_MESSAGE_INFO 0x02
struct qmi_uim_param_message_info {
uint8_t pin_id;
uint8_t length;
uint8_t pin_value[0];
} __attribute__((__packed__));

View File

@@ -0,0 +1,25 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 Kerlink SA. 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.
*
*/
#define QMI_WDA_SET_DATA_FORMAT 32 /* Set data format */
#define QMI_WDA_GET_DATA_FORMAT 33 /* Get data format */
/* Get and set data format interface */
#define QMI_WDA_LL_PROTOCOL 0x11 /* uint32_t */
#define QMI_WDA_DATA_LINK_PROTOCOL_UNKNOWN 0
#define QMI_WDA_DATA_LINK_PROTOCOL_802_3 1
#define QMI_WDA_DATA_LINK_PROTOCOL_RAW_IP 2

View File

@@ -30,6 +30,13 @@
/* Start WDS network interface */
#define QMI_WDS_PARAM_APN 0x14 /* string */
#define QMI_WDS_PARAM_IP_FAMILY 0x19 /* uint8 */
#define QMI_WDS_PARAM_USERNAME 0x17 /* string */
#define QMI_WDS_PARAM_PASSWORD 0x18 /* string */
#define QMI_WDS_PARAM_AUTHENTICATION_PREFERENCE 0x16 /* uint8 */
#define QMI_WDS_AUTHENTICATION_NONE 0x0
#define QMI_WDS_AUTHENTICATION_PAP 0x1
#define QMI_WDS_AUTHENTICATION_CHAP 0x2
#define QMI_WDS_RESULT_PKT_HANDLE 0x01 /* uint32 */
@@ -51,10 +58,12 @@ struct qmi_wds_notify_conn_status {
/* Get the runtime data session settings */
#define QMI_WDS_RESULT_PDP_TYPE 0x11 /* uint8 */
#define QMI_WDS_RESULT_APN 0x14 /* string */
#define QMI_WDS_RESULT_PRIMARY_DNS 0x15 /* uint32 IPv4 */
#define QMI_WDS_RESULT_SECONDARY_DNS 0x16 /* uint32 IPv4 */
#define QMI_WDS_RESULT_IP_ADDRESS 0x1e /* uint32 IPv4 */
#define QMI_WDS_RESULT_GATEWAY 0x20 /* uint32 IPv4 */
#define QMI_WDS_RESULT_GATEWAY_NETMASK 0x21 /* uint32 IPv4 */
#define QMI_WDS_RESULT_IP_FAMILY 0x2b /* uint8 */
#define QMI_WDS_PDP_TYPE_IPV4 0x00

View File

@@ -31,7 +31,9 @@
typedef GObjectClass RilCellInfoClass;
typedef struct ril_cell_info RilCellInfo;
struct ril_cell_info_priv {
struct ril_cell_info {
GObject object;
struct sailfish_cell_info info;
GRilIoChannel *io;
MceDisplay *display;
struct ril_radio *radio;
@@ -60,63 +62,22 @@ G_DEFINE_TYPE(RilCellInfo, ril_cell_info, G_TYPE_OBJECT)
#define RIL_CELL_INFO(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj),\
RIL_CELL_INFO_TYPE, RilCellInfo))
#define DBG_(self,fmt,args...) DBG("%s" fmt, (self)->priv->log_prefix, ##args)
#define DBG_(self,fmt,args...) DBG("%s" fmt, (self)->log_prefix, ##args)
gint ril_cell_compare_location(const struct ril_cell *c1,
const struct ril_cell *c2)
static inline void ril_cell_free(struct sailfish_cell *cell)
{
if (c1 && c2) {
if (c1->type != c2->type) {
return c1->type - c2->type;
} else if (c1->type == RIL_CELL_INFO_TYPE_GSM) {
const struct ril_cell_info_gsm *g1 = &c1->info.gsm;
const struct ril_cell_info_gsm *g2 = &c2->info.gsm;
if (g1->lac != g2->lac) {
return g1->lac - g2->lac;
} else {
return g1->cid - g2->cid;
}
} else if (c2->type == RIL_CELL_INFO_TYPE_WCDMA) {
const struct ril_cell_info_wcdma *w1 = &c1->info.wcdma;
const struct ril_cell_info_wcdma *w2 = &c2->info.wcdma;
if (w1->lac != w2->lac) {
return w1->lac - w2->lac;
} else {
return w1->cid - w2->cid;
}
} else {
const struct ril_cell_info_lte *l1 = &c1->info.lte;
const struct ril_cell_info_lte *l2 = &c2->info.lte;
GASSERT(c1->type == RIL_CELL_INFO_TYPE_LTE);
if (l1->ci != l2->ci) {
return l1->ci - l2->ci;
} else if (l1->pci != l2->pci) {
return l1->pci - l2->pci;
} else {
return l1->tac - l2->tac;
}
}
} else if (c1) {
return 1;
} else if (c2) {
return -1;
} else {
return 0;
}
g_slice_free(struct sailfish_cell, cell);
}
gint ril_cell_compare_func(gconstpointer v1, gconstpointer v2)
static void ril_cell_free1(gpointer cell)
{
return ril_cell_compare_location(v1, v2);
ril_cell_free(cell);
}
static gboolean ril_cell_info_list_identical(GSList *l1, GSList *l2)
{
while (l1 && l2) {
if (memcmp(l1->data, l2->data, sizeof(struct ril_cell))) {
if (memcmp(l1->data, l2->data, sizeof(struct sailfish_cell))) {
return FALSE;
}
l1 = l1->next;
@@ -127,21 +88,21 @@ static gboolean ril_cell_info_list_identical(GSList *l1, GSList *l2)
static void ril_cell_info_update_cells(struct ril_cell_info *self, GSList *l)
{
if (!ril_cell_info_list_identical(self->cells, l)) {
g_slist_free_full(self->cells, g_free);
self->cells = l;
g_signal_emit(self, ril_cell_info_signals[
SIGNAL_CELLS_CHANGED], 0);
if (!ril_cell_info_list_identical(self->info.cells, l)) {
g_slist_free_full(self->info.cells, ril_cell_free1);
self->info.cells = l;
g_signal_emit(self, ril_cell_info_signals
[SIGNAL_CELLS_CHANGED], 0);
} else {
g_slist_free_full(l, g_free);
g_slist_free_full(l, ril_cell_free1);
}
}
static struct ril_cell *ril_cell_info_parse_cell_gsm(GRilIoParser *rilp,
static struct sailfish_cell *ril_cell_info_parse_cell_gsm(GRilIoParser *rilp,
guint version, gboolean registered)
{
struct ril_cell *cell = g_new0(struct ril_cell, 1);
struct ril_cell_info_gsm *gsm = &cell->info.gsm;
struct sailfish_cell *cell = g_slice_new0(struct sailfish_cell);
struct sailfish_cell_info_gsm *gsm = &cell->info.gsm;
/* Optional RIL_CellIdentityGsm_v12 part */
gsm->arfcn = INT_MAX;
@@ -166,21 +127,21 @@ static struct ril_cell *ril_cell_info_parse_cell_gsm(GRilIoParser *rilp,
gsm->mcc, gsm->mnc, gsm->lac, gsm->cid, gsm->arfcn,
gsm->bsic, gsm->signalStrength, gsm->bitErrorRate,
gsm->timingAdvance);
cell->type = RIL_CELL_INFO_TYPE_GSM;
cell->type = SAILFISH_CELL_TYPE_GSM;
cell->registered = registered;
return cell;
}
ofono_error("failed to parse GSM cell info");
g_free(cell);
ril_cell_free(cell);
return NULL;
}
static struct ril_cell *ril_cell_info_parse_cell_wcdma(GRilIoParser *rilp,
static struct sailfish_cell *ril_cell_info_parse_cell_wcdma(GRilIoParser *rilp,
guint version, gboolean registered)
{
struct ril_cell *cell = g_new0(struct ril_cell, 1);
struct ril_cell_info_wcdma *wcdma = &cell->info.wcdma;
struct sailfish_cell *cell = g_slice_new0(struct sailfish_cell);
struct sailfish_cell_info_wcdma *wcdma = &cell->info.wcdma;
/* Optional RIL_CellIdentityWcdma_v12 part */
wcdma->uarfcn = INT_MAX;
@@ -197,21 +158,21 @@ static struct ril_cell *ril_cell_info_parse_cell_wcdma(GRilIoParser *rilp,
"strength=%d,err=%d", registered, wcdma->mcc,
wcdma->mnc, wcdma->lac, wcdma->cid, wcdma->psc,
wcdma->signalStrength, wcdma->bitErrorRate);
cell->type = RIL_CELL_INFO_TYPE_WCDMA;
cell->type = SAILFISH_CELL_TYPE_WCDMA;
cell->registered = registered;
return cell;
}
ofono_error("failed to parse WCDMA cell info");
g_free(cell);
ril_cell_free(cell);
return NULL;
}
static struct ril_cell *ril_cell_info_parse_cell_lte(GRilIoParser *rilp,
static struct sailfish_cell *ril_cell_info_parse_cell_lte(GRilIoParser *rilp,
guint version, gboolean registered)
{
struct ril_cell *cell = g_new0(struct ril_cell, 1);
struct ril_cell_info_lte *lte = &cell->info.lte;
struct sailfish_cell *cell = g_slice_new0(struct sailfish_cell);
struct sailfish_cell_info_lte *lte = &cell->info.lte;
/* Optional RIL_CellIdentityLte_v12 part */
lte->earfcn = INT_MAX;
@@ -233,18 +194,18 @@ static struct ril_cell *ril_cell_info_parse_cell_lte(GRilIoParser *rilp,
"t=0x%x", registered, lte->mcc, lte->mnc, lte->ci,
lte->pci, lte->tac, lte->signalStrength, lte->rsrp,
lte->rsrq, lte->rssnr, lte->cqi, lte->timingAdvance);
cell->type = RIL_CELL_INFO_TYPE_LTE;
cell->type = SAILFISH_CELL_TYPE_LTE;
cell->registered = registered;
return cell;
}
ofono_error("failed to parse LTE cell info");
g_free(cell);
ril_cell_free(cell);
return NULL;
}
static gboolean ril_cell_info_parse_cell(GRilIoParser *rilp, guint v,
struct ril_cell **cell_ptr)
struct sailfish_cell **cell_ptr)
{
int type, reg;
@@ -253,7 +214,7 @@ static gboolean ril_cell_info_parse_cell(GRilIoParser *rilp, guint v,
/* Skip timestamp */
grilio_parser_get_int32_array(rilp, NULL, 3)) {
int skip = 0;
struct ril_cell *cell = NULL;
struct sailfish_cell *cell = NULL;
/* Normalize the boolean value */
reg = (reg != FALSE);
@@ -302,13 +263,13 @@ static GSList *ril_cell_info_parse_list(guint v, const void *data, guint len)
grilio_parser_init(&rilp, data, len);
if (grilio_parser_get_int32(&rilp, &n) && n > 0) {
struct ril_cell *c;
struct sailfish_cell *c;
DBG("%d cell(s):", n);
for (i=0; i<n && ril_cell_info_parse_cell(&rilp, v, &c); i++) {
if (c) {
l = g_slist_insert_sorted(l, c,
ril_cell_compare_func);
sailfish_cell_compare_func);
}
}
}
@@ -331,11 +292,10 @@ static void ril_cell_info_list_cb(GRilIoChannel *io, int status,
const void *data, guint len, void *user_data)
{
struct ril_cell_info *self = RIL_CELL_INFO(user_data);
struct ril_cell_info_priv *priv = self->priv;
DBG_(self, "");
GASSERT(priv->query_id);
priv->query_id = 0;
GASSERT(self->query_id);
self->query_id = 0;
ril_cell_info_update_cells(self, ril_cell_info_parse_list
(io->ril_version, data, len));
}
@@ -344,21 +304,19 @@ static void ril_cell_info_set_rate_cb(GRilIoChannel *io, int status,
const void *data, guint len, void *user_data)
{
struct ril_cell_info *self = RIL_CELL_INFO(user_data);
struct ril_cell_info_priv *priv = self->priv;
DBG_(self, "");
GASSERT(priv->set_rate_id);
priv->set_rate_id = 0;
GASSERT(self->set_rate_id);
self->set_rate_id = 0;
}
static void ril_cell_info_query(struct ril_cell_info *self)
{
struct ril_cell_info_priv *priv = self->priv;
GRilIoRequest *req = grilio_request_new();
grilio_request_set_retry(req, RIL_RETRY_MS, -1);
grilio_channel_cancel_request(priv->io, priv->query_id, FALSE);
priv->query_id = grilio_channel_send_request_full(priv->io, req,
grilio_channel_cancel_request(self->io, self->query_id, FALSE);
self->query_id = grilio_channel_send_request_full(self->io, req,
RIL_REQUEST_GET_CELL_INFO_LIST, ril_cell_info_list_cb,
NULL, self);
grilio_request_unref(req);
@@ -366,14 +324,13 @@ static void ril_cell_info_query(struct ril_cell_info *self)
static void ril_cell_info_set_rate(struct ril_cell_info *self, int ms)
{
struct ril_cell_info_priv *priv = self->priv;
GRilIoRequest *req = grilio_request_sized_new(8);
grilio_request_append_int32(req, 1);
grilio_request_append_int32(req, ms);
grilio_request_set_retry(req, RIL_RETRY_MS, -1);
grilio_channel_cancel_request(priv->io, priv->set_rate_id, FALSE);
priv->set_rate_id = grilio_channel_send_request_full(priv->io, req,
grilio_channel_cancel_request(self->io, self->set_rate_id, FALSE);
self->set_rate_id = grilio_channel_send_request_full(self->io, req,
RIL_REQUEST_SET_UNSOL_CELL_INFO_LIST_RATE,
ril_cell_info_set_rate_cb, NULL, self);
grilio_request_unref(req);
@@ -381,29 +338,24 @@ static void ril_cell_info_set_rate(struct ril_cell_info *self, int ms)
static void ril_cell_info_update_rate(struct ril_cell_info *self)
{
struct ril_cell_info_priv *priv = self->priv;
ril_cell_info_set_rate(self,
(priv->display->state == MCE_DISPLAY_STATE_OFF) ?
(self->display->state == MCE_DISPLAY_STATE_OFF) ?
DISPLAY_OFF_UPDATE_RATE : DISPLAY_ON_UPDATE_RATE);
}
static void ril_cell_info_display_state_cb(MceDisplay *display, void *arg)
{
struct ril_cell_info *self = RIL_CELL_INFO(arg);
struct ril_cell_info_priv *priv = self->priv;
if (priv->sim_card_ready) {
if (self->sim_card_ready) {
ril_cell_info_update_rate(self);
}
}
static void ril_cell_info_refresh(struct ril_cell_info *self)
{
struct ril_cell_info_priv *priv = self->priv;
/* RIL_REQUEST_GET_CELL_INFO_LIST fails without SIM card */
if (priv->radio->state == RADIO_STATE_ON && priv->sim_card_ready) {
if (self->radio->state == RADIO_STATE_ON && self->sim_card_ready) {
ril_cell_info_query(self);
} else {
ril_cell_info_update_cells(self, NULL);
@@ -421,124 +373,160 @@ static void ril_cell_info_radio_state_cb(struct ril_radio *radio, void *arg)
static void ril_cell_info_sim_status_cb(struct ril_sim_card *sim, void *arg)
{
struct ril_cell_info *self = RIL_CELL_INFO(arg);
struct ril_cell_info_priv *priv = self->priv;
const gboolean sim_card_was_ready = priv->sim_card_ready;
const gboolean sim_card_was_ready = self->sim_card_ready;
DBG_(self, "%sready", ril_sim_card_ready(sim) ? "" : "not ");
priv->sim_card_ready = ril_sim_card_ready(sim);
if (priv->sim_card_ready != sim_card_was_ready) {
self->sim_card_ready = ril_sim_card_ready(sim);
if (self->sim_card_ready != sim_card_was_ready) {
ril_cell_info_refresh(self);
if (priv->sim_card_ready) {
if (self->sim_card_ready) {
ril_cell_info_update_rate(self);
}
}
}
gulong ril_cell_info_add_cells_changed_handler(struct ril_cell_info *self,
ril_cell_info_cb_t cb, void *arg)
/* sailfish_cell_info interface callbacks */
struct ril_cell_info_signal_data {
sailfish_cell_info_cb_t cb;
void *arg;
};
static inline struct ril_cell_info *ril_cell_info_cast
(struct sailfish_cell_info *info)
{
return (G_LIKELY(self) && G_LIKELY(cb)) ? g_signal_connect(self,
SIGNAL_CELLS_CHANGED_NAME, G_CALLBACK(cb), arg) : 0;
return G_CAST(info, struct ril_cell_info, info);
}
void ril_cell_info_remove_handler(struct ril_cell_info *self, gulong id)
static void ril_cell_info_ref_proc(struct sailfish_cell_info *info)
{
if (G_LIKELY(self) && G_LIKELY(id)) {
g_signal_handler_disconnect(self, id);
g_object_ref(ril_cell_info_cast(info));
}
static void ril_cell_info_unref_proc(struct sailfish_cell_info *info)
{
g_object_unref(ril_cell_info_cast(info));
}
static void ril_cell_info_cells_changed_cb(struct ril_cell_info *self,
void *user_data)
{
struct ril_cell_info_signal_data *data = user_data;
data->cb(&self->info, data->arg);
}
static void ril_cell_info_cells_disconnect_notify(gpointer data,
GClosure *closure)
{
g_slice_free1(sizeof(struct ril_cell_info_signal_data), data);
}
static gulong ril_cell_info_add_cells_changed_handler_proc
(struct sailfish_cell_info *info,
sailfish_cell_info_cb_t cb, void *arg)
{
if (cb) {
struct ril_cell_info_signal_data *data =
g_slice_new(struct ril_cell_info_signal_data);
data->cb = cb;
data->arg = arg;
return g_signal_connect_data(ril_cell_info_cast(info),
SIGNAL_CELLS_CHANGED_NAME,
G_CALLBACK(ril_cell_info_cells_changed_cb),
data, ril_cell_info_cells_disconnect_notify,
G_CONNECT_AFTER);
} else {
return 0;
}
}
struct ril_cell_info *ril_cell_info_new(GRilIoChannel *io,
static void ril_cell_info_remove_handler_proc(struct sailfish_cell_info *info,
gulong id)
{
if (G_LIKELY(id)) {
g_signal_handler_disconnect(ril_cell_info_cast(info), id);
}
}
struct sailfish_cell_info *ril_cell_info_new(GRilIoChannel *io,
const char *log_prefix, MceDisplay *display,
struct ril_radio *radio, struct ril_sim_card *sim_card)
{
struct ril_cell_info *self = g_object_new(RIL_CELL_INFO_TYPE, 0);
struct ril_cell_info_priv *priv = self->priv;
static const struct sailfish_cell_info_proc ril_cell_info_proc = {
ril_cell_info_ref_proc,
ril_cell_info_unref_proc,
ril_cell_info_add_cells_changed_handler_proc,
ril_cell_info_remove_handler_proc
};
priv->io = grilio_channel_ref(io);
priv->display = mce_display_ref(display);
priv->radio = ril_radio_ref(radio);
priv->sim_card = ril_sim_card_ref(sim_card);
priv->log_prefix = (log_prefix && log_prefix[0]) ?
struct ril_cell_info *self = g_object_new(RIL_CELL_INFO_TYPE, 0);
self->info.proc = &ril_cell_info_proc;
self->io = grilio_channel_ref(io);
self->display = mce_display_ref(display);
self->radio = ril_radio_ref(radio);
self->sim_card = ril_sim_card_ref(sim_card);
self->log_prefix = (log_prefix && log_prefix[0]) ?
g_strconcat(log_prefix, " ", NULL) : g_strdup("");
DBG_(self, "");
priv->event_id = grilio_channel_add_unsol_event_handler(priv->io,
self->event_id = grilio_channel_add_unsol_event_handler(self->io,
ril_cell_info_list_changed_cb, RIL_UNSOL_CELL_INFO_LIST, self);
priv->display_state_event_id =
self->display_state_event_id =
mce_display_add_state_changed_handler(display,
ril_cell_info_display_state_cb, self);
priv->radio_state_event_id =
self->radio_state_event_id =
ril_radio_add_state_changed_handler(radio,
ril_cell_info_radio_state_cb, self);
priv->sim_status_event_id =
ril_sim_card_add_status_changed_handler(priv->sim_card,
self->sim_status_event_id =
ril_sim_card_add_status_changed_handler(self->sim_card,
ril_cell_info_sim_status_cb, self);
priv->sim_card_ready = ril_sim_card_ready(sim_card);
if (priv->sim_card_ready) {
self->sim_card_ready = ril_sim_card_ready(sim_card);
if (self->sim_card_ready) {
ril_cell_info_query(self);
ril_cell_info_update_rate(self);
}
return self;
}
struct ril_cell_info *ril_cell_info_ref(struct ril_cell_info *self)
{
if (G_LIKELY(self)) {
g_object_ref(RIL_CELL_INFO(self));
return self;
} else {
return NULL;
}
}
void ril_cell_info_unref(struct ril_cell_info *self)
{
if (G_LIKELY(self)) {
g_object_unref(RIL_CELL_INFO(self));
}
return &self->info;
}
static void ril_cell_info_init(struct ril_cell_info *self)
{
self->priv = G_TYPE_INSTANCE_GET_PRIVATE(self, RIL_CELL_INFO_TYPE,
struct ril_cell_info_priv);
}
static void ril_cell_info_dispose(GObject *object)
{
struct ril_cell_info *self = RIL_CELL_INFO(object);
struct ril_cell_info_priv *priv = self->priv;
grilio_channel_remove_handlers(priv->io, &priv->event_id, 1);
if (priv->query_id) {
grilio_channel_cancel_request(priv->io, priv->query_id, FALSE);
priv->query_id = 0;
grilio_channel_remove_handlers(self->io, &self->event_id, 1);
if (self->query_id) {
grilio_channel_cancel_request(self->io, self->query_id, FALSE);
self->query_id = 0;
}
if (priv->set_rate_id) {
grilio_channel_cancel_request(priv->io, priv->set_rate_id,
if (self->set_rate_id) {
grilio_channel_cancel_request(self->io, self->set_rate_id,
FALSE);
priv->set_rate_id = 0;
self->set_rate_id = 0;
}
gutil_disconnect_handlers(priv->display,
&priv->display_state_event_id, 1);
ril_radio_remove_handlers(priv->radio, &priv->radio_state_event_id, 1);
ril_sim_card_remove_handlers(priv->sim_card,
&priv->sim_status_event_id, 1);
gutil_disconnect_handlers(self->display,
&self->display_state_event_id, 1);
ril_radio_remove_handlers(self->radio, &self->radio_state_event_id, 1);
ril_sim_card_remove_handlers(self->sim_card,
&self->sim_status_event_id, 1);
G_OBJECT_CLASS(ril_cell_info_parent_class)->dispose(object);
}
static void ril_cell_info_finalize(GObject *object)
{
struct ril_cell_info *self = RIL_CELL_INFO(object);
struct ril_cell_info_priv *priv = self->priv;
DBG_(self, "");
g_free(priv->log_prefix);
grilio_channel_unref(priv->io);
mce_display_unref(priv->display);
ril_radio_unref(priv->radio);
ril_sim_card_unref(priv->sim_card);
g_slist_free_full(self->cells, g_free);
g_free(self->log_prefix);
grilio_channel_unref(self->io);
mce_display_unref(self->display);
ril_radio_unref(self->radio);
ril_sim_card_unref(self->sim_card);
g_slist_free_full(self->info.cells, ril_cell_free1);
G_OBJECT_CLASS(ril_cell_info_parent_class)->finalize(object);
}
@@ -548,7 +536,6 @@ static void ril_cell_info_class_init(RilCellInfoClass *klass)
object_class->dispose = ril_cell_info_dispose;
object_class->finalize = ril_cell_info_finalize;
g_type_class_add_private(klass, sizeof(struct ril_cell_info_priv));
ril_cell_info_signals[SIGNAL_CELLS_CHANGED] =
g_signal_new(SIGNAL_CELLS_CHANGED_NAME,
G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST,

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,40 +18,11 @@
#include "ril_types.h"
#include <mce_display.h>
#include <sailfish_cell_info.h>
struct ril_cell {
enum ril_cell_info_type type;
gboolean registered;
union {
struct ril_cell_info_gsm gsm;
struct ril_cell_info_wcdma wcdma;
struct ril_cell_info_lte lte;
} info;
};
struct ril_cell_info_priv;
struct ril_cell_info {
GObject object;
struct ril_cell_info_priv *priv;
GSList *cells;
};
typedef void (*ril_cell_info_cb_t)(struct ril_cell_info *info, void *arg);
gint ril_cell_compare_func(gconstpointer v1, gconstpointer v2);
gint ril_cell_compare_location(const struct ril_cell *c1,
const struct ril_cell *c2);
struct ril_cell_info *ril_cell_info_new(GRilIoChannel *io,
struct sailfish_cell_info *ril_cell_info_new(GRilIoChannel *io,
const char *log_prefix, MceDisplay *display,
struct ril_radio *radio, struct ril_sim_card *sim_card);
struct ril_cell_info *ril_cell_info_ref(struct ril_cell_info *info);
void ril_cell_info_unref(struct ril_cell_info *info);
struct ril_cell *ril_cell_find_cell(struct ril_cell_info *info,
const struct ril_cell *cell);
gulong ril_cell_info_add_cells_changed_handler(struct ril_cell_info *info,
ril_cell_info_cb_t cb, void *arg);
void ril_cell_info_remove_handler(struct ril_cell_info *info, gulong id);
#endif /* RIL_CELL_INFO_H */

View File

@@ -1,587 +0,0 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* 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.
*/
#include "ril_plugin.h"
#include "ril_cell_info.h"
#include "ril_log.h"
#include <ofono/dbus.h>
#include <gdbus.h>
struct ril_cell_entry {
guint cell_id;
char *path;
struct ril_cell cell;
};
struct ril_cell_info_dbus {
struct ril_cell_info *info;
DBusConnection *conn;
char *path;
gulong handler_id;
guint next_cell_id;
GSList *entries;
};
#define RIL_CELL_INFO_DBUS_INTERFACE "org.nemomobile.ofono.CellInfo"
#define RIL_CELL_INFO_DBUS_CELLS_ADDED_SIGNAL "CellsAdded"
#define RIL_CELL_INFO_DBUS_CELLS_REMOVED_SIGNAL "CellsRemoved"
#define RIL_CELL_DBUS_INTERFACE_VERSION (1)
#define RIL_CELL_DBUS_INTERFACE "org.nemomobile.ofono.Cell"
#define RIL_CELL_DBUS_REGISTERED_CHANGED_SIGNAL "RegisteredChanged"
#define RIL_CELL_DBUS_PROPERTY_CHANGED_SIGNAL "PropertyChanged"
#define RIL_CELL_DBUS_REMOVED_SIGNAL "Removed"
struct ril_cell_property {
const char *name;
glong off;
int flag;
};
#define RIL_CELL_GSM_PROPERTY(value,name) \
{ #name, G_STRUCT_OFFSET(struct ril_cell_info_gsm,name), value }
#define RIL_CELL_WCDMA_PROPERTY(value,name) \
{ #name, G_STRUCT_OFFSET(struct ril_cell_info_wcdma,name), value }
#define RIL_CELL_LTE_PROPERTY(value,name) \
{ #name, G_STRUCT_OFFSET(struct ril_cell_info_lte,name), value }
static const struct ril_cell_property ril_cell_gsm_properties [] = {
RIL_CELL_GSM_PROPERTY(0x001,mcc),
RIL_CELL_GSM_PROPERTY(0x002,mnc),
RIL_CELL_GSM_PROPERTY(0x004,lac),
RIL_CELL_GSM_PROPERTY(0x008,cid),
RIL_CELL_GSM_PROPERTY(0x010,arfcn),
RIL_CELL_GSM_PROPERTY(0x020,bsic),
RIL_CELL_GSM_PROPERTY(0x040,signalStrength),
RIL_CELL_GSM_PROPERTY(0x080,bitErrorRate),
RIL_CELL_GSM_PROPERTY(0x100,timingAdvance)
};
static const struct ril_cell_property ril_cell_wcdma_properties [] = {
RIL_CELL_WCDMA_PROPERTY(0x01,mcc),
RIL_CELL_WCDMA_PROPERTY(0x02,mnc),
RIL_CELL_WCDMA_PROPERTY(0x04,lac),
RIL_CELL_WCDMA_PROPERTY(0x08,cid),
RIL_CELL_WCDMA_PROPERTY(0x10,psc),
RIL_CELL_WCDMA_PROPERTY(0x20,uarfcn),
RIL_CELL_WCDMA_PROPERTY(0x40,signalStrength),
RIL_CELL_WCDMA_PROPERTY(0x80,bitErrorRate)
};
static const struct ril_cell_property ril_cell_lte_properties [] = {
RIL_CELL_LTE_PROPERTY(0x001,mcc),
RIL_CELL_LTE_PROPERTY(0x002,mnc),
RIL_CELL_LTE_PROPERTY(0x004,ci),
RIL_CELL_LTE_PROPERTY(0x008,pci),
RIL_CELL_LTE_PROPERTY(0x010,tac),
RIL_CELL_LTE_PROPERTY(0x020,earfcn),
RIL_CELL_LTE_PROPERTY(0x040,signalStrength),
RIL_CELL_LTE_PROPERTY(0x080,rsrp),
RIL_CELL_LTE_PROPERTY(0x100,rsrq),
RIL_CELL_LTE_PROPERTY(0x200,rssnr),
RIL_CELL_LTE_PROPERTY(0x400,cqi),
RIL_CELL_LTE_PROPERTY(0x800,timingAdvance)
};
#define RIL_CELL_PROPERTY_REGISTERED 0x1000
typedef void (*ril_cell_info_dbus_append_fn)(DBusMessageIter *it,
const struct ril_cell_entry *entry);
static const char *ril_cell_info_dbus_cell_type_str(enum ril_cell_info_type t)
{
switch (t) {
case RIL_CELL_INFO_TYPE_GSM:
return "gsm";
case RIL_CELL_INFO_TYPE_CDMA:
return "cdma";
case RIL_CELL_INFO_TYPE_LTE:
return "lte";
case RIL_CELL_INFO_TYPE_WCDMA:
return "wcdma";
case RIL_CELL_INFO_TYPE_TD_SCDMA:
return "tdscdma";
case RIL_CELL_INFO_TYPE_NONE:
default:
return "unknown";
}
};
static const struct ril_cell_property *ril_cell_info_dbus_cell_properties(
enum ril_cell_info_type type, int *count)
{
switch (type) {
case RIL_CELL_INFO_TYPE_GSM:
*count = G_N_ELEMENTS(ril_cell_gsm_properties);
return ril_cell_gsm_properties;
case RIL_CELL_INFO_TYPE_WCDMA:
*count = G_N_ELEMENTS(ril_cell_wcdma_properties);
return ril_cell_wcdma_properties;
case RIL_CELL_INFO_TYPE_LTE:
*count = G_N_ELEMENTS(ril_cell_lte_properties);
return ril_cell_lte_properties;
default:
*count = 0;
return NULL;
}
};
static void ril_cell_info_destroy_entry(struct ril_cell_entry *entry)
{
if (entry) {
g_free(entry->path);
g_free(entry);
}
}
static DBusMessage *ril_cell_info_dbus_reply(DBusMessage *msg,
const struct ril_cell_entry *entry,
ril_cell_info_dbus_append_fn append)
{
DBusMessage *reply = dbus_message_new_method_return(msg);
DBusMessageIter it;
dbus_message_iter_init_append(reply, &it);
append(&it, entry);
return reply;
}
static void ril_cell_info_dbus_append_version(DBusMessageIter *it,
const struct ril_cell_entry *entry)
{
dbus_int32_t version = RIL_CELL_DBUS_INTERFACE_VERSION;
dbus_message_iter_append_basic(it, DBUS_TYPE_INT32, &version);
}
static void ril_cell_info_dbus_append_type(DBusMessageIter *it,
const struct ril_cell_entry *entry)
{
const char *type = ril_cell_info_dbus_cell_type_str(entry->cell.type);
dbus_message_iter_append_basic(it, DBUS_TYPE_STRING, &type);
}
static void ril_cell_info_dbus_append_registered(DBusMessageIter *it,
const struct ril_cell_entry *entry)
{
dbus_bool_t registered = entry->cell.registered;
dbus_message_iter_append_basic(it, DBUS_TYPE_BOOLEAN, &registered);
}
static void ril_cell_info_dbus_append_properties(DBusMessageIter *it,
const struct ril_cell_entry *entry)
{
int i, n;
DBusMessageIter dict;
const struct ril_cell *cell = &entry->cell;
const struct ril_cell_property *prop =
ril_cell_info_dbus_cell_properties(cell->type, &n);
dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY, "{sv}", &dict);
for (i = 0; i < n; i++) {
gint32 value = G_STRUCT_MEMBER(int, &cell->info, prop[i].off);
if (value != INT_MAX) {
ofono_dbus_dict_append(&dict, prop[i].name,
DBUS_TYPE_INT32, &value);
}
}
dbus_message_iter_close_container(it, &dict);
}
static void ril_cell_info_dbus_append_all(DBusMessageIter *it,
const struct ril_cell_entry *entry)
{
ril_cell_info_dbus_append_version(it, entry);
ril_cell_info_dbus_append_type(it, entry);
ril_cell_info_dbus_append_registered(it, entry);
ril_cell_info_dbus_append_properties(it, entry);
}
static DBusMessage *ril_cell_info_dbus_cell_get_all(DBusConnection *conn,
DBusMessage *msg, void *data)
{
return ril_cell_info_dbus_reply(msg, (struct ril_cell_entry*)data,
ril_cell_info_dbus_append_all);
}
static DBusMessage *ril_cell_info_dbus_cell_get_version(DBusConnection *conn,
DBusMessage *msg, void *data)
{
return ril_cell_info_dbus_reply(msg, (struct ril_cell_entry*)data,
ril_cell_info_dbus_append_version);
}
static DBusMessage *ril_cell_info_dbus_cell_get_type(DBusConnection *conn,
DBusMessage *msg, void *data)
{
return ril_cell_info_dbus_reply(msg, (struct ril_cell_entry*)data,
ril_cell_info_dbus_append_type);
}
static DBusMessage *ril_cell_info_dbus_cell_get_registered(DBusConnection *conn,
DBusMessage *msg, void *data)
{
return ril_cell_info_dbus_reply(msg, (struct ril_cell_entry*)data,
ril_cell_info_dbus_append_registered);
}
static DBusMessage *ril_cell_info_dbus_cell_get_properties(DBusConnection *conn,
DBusMessage *msg, void *data)
{
return ril_cell_info_dbus_reply(msg, (struct ril_cell_entry*)data,
ril_cell_info_dbus_append_properties);
}
static const GDBusMethodTable ril_cell_info_dbus_cell_methods[] = {
{ GDBUS_METHOD("GetAll", NULL,
GDBUS_ARGS({ "version", "i" },
{ "type", "s" },
{ "registered", "b" },
{ "properties", "a{sv}" }),
ril_cell_info_dbus_cell_get_all) },
{ GDBUS_METHOD("GetInterfaceVersion", NULL,
GDBUS_ARGS({ "version", "i" }),
ril_cell_info_dbus_cell_get_version) },
{ GDBUS_METHOD("GetType", NULL,
GDBUS_ARGS({ "type", "s" }),
ril_cell_info_dbus_cell_get_type) },
{ GDBUS_METHOD("GetRegistered", NULL,
GDBUS_ARGS({ "registered", "b" }),
ril_cell_info_dbus_cell_get_registered) },
{ GDBUS_METHOD("GetProperties", NULL,
GDBUS_ARGS({ "properties", "a{sv}" }),
ril_cell_info_dbus_cell_get_properties) },
{ }
};
static const GDBusSignalTable ril_cell_info_dbus_cell_signals[] = {
{ GDBUS_SIGNAL(RIL_CELL_DBUS_REGISTERED_CHANGED_SIGNAL,
GDBUS_ARGS({ "registered", "b" })) },
{ GDBUS_SIGNAL(RIL_CELL_DBUS_PROPERTY_CHANGED_SIGNAL,
GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
{ GDBUS_SIGNAL(RIL_CELL_DBUS_REMOVED_SIGNAL,
GDBUS_ARGS({})) },
{ }
};
static struct ril_cell_entry *ril_cell_info_dbus_find_id(
struct ril_cell_info_dbus *dbus, guint id)
{
GSList *l;
for (l = dbus->entries; l; l = l->next) {
struct ril_cell_entry *entry = l->data;
if (entry->cell_id == id) {
return entry;
}
}
return NULL;
}
static guint ril_cell_info_dbus_next_cell_id(struct ril_cell_info_dbus *dbus)
{
while (ril_cell_info_dbus_find_id(dbus, dbus->next_cell_id)) {
dbus->next_cell_id++;
}
return dbus->next_cell_id++;
}
static struct ril_cell_entry *ril_cell_info_dbus_find_cell(
struct ril_cell_info_dbus *dbus, const struct ril_cell *cell)
{
if (cell) {
GSList *l;
for (l = dbus->entries; l; l = l->next) {
struct ril_cell_entry *entry = l->data;
if (!ril_cell_compare_location(&entry->cell, cell)) {
return entry;
}
}
}
return NULL;
}
static void ril_cell_info_dbus_emit_path_list(struct ril_cell_info_dbus *dbus,
const char *name, GPtrArray *list)
{
guint i;
DBusMessageIter it, array;
DBusMessage *signal = dbus_message_new_signal(dbus->path,
RIL_CELL_INFO_DBUS_INTERFACE, name);
dbus_message_iter_init_append(signal, &it);
dbus_message_iter_open_container(&it, DBUS_TYPE_ARRAY, "o", &array);
for (i = 0; i < list->len; i++) {
const char* path = list->pdata[i];
dbus_message_iter_append_basic(&array, DBUS_TYPE_OBJECT_PATH,
&path);
}
dbus_message_iter_close_container(&it, &array);
g_dbus_send_message(dbus->conn, signal);
}
static int ril_cell_info_dbus_compare(const struct ril_cell *c1,
const struct ril_cell *c2)
{
if (c1->type == c2->type) {
int i, n, mask = 0;
const struct ril_cell_property *prop =
ril_cell_info_dbus_cell_properties(c1->type, &n);
if (c1->registered != c2->registered) {
mask |= RIL_CELL_PROPERTY_REGISTERED;
}
for (i = 0; i < n; i++) {
const glong offset = prop[i].off;
gint32 v1 = G_STRUCT_MEMBER(int, &c1->info, offset);
gint32 v2 = G_STRUCT_MEMBER(int, &c2->info, offset);
if (v1 != v2) {
mask |= prop[i].flag;
}
}
return mask;
} else {
return -1;
}
}
static void ril_cell_info_dbus_property_changed(struct ril_cell_info_dbus *dbus,
const struct ril_cell_entry *entry, int mask)
{
int i, n;
const struct ril_cell *cell = &entry->cell;
const struct ril_cell_property *prop =
ril_cell_info_dbus_cell_properties(cell->type, &n);
if (mask & RIL_CELL_PROPERTY_REGISTERED) {
dbus_bool_t registered = cell->registered;
g_dbus_emit_signal(dbus->conn, entry->path,
RIL_CELL_DBUS_INTERFACE,
RIL_CELL_DBUS_REGISTERED_CHANGED_SIGNAL,
DBUS_TYPE_BOOLEAN, &registered, DBUS_TYPE_INVALID);
mask &= ~RIL_CELL_PROPERTY_REGISTERED;
}
for (i = 0; i < n && mask; i++) {
if (mask & prop[i].flag) {
ofono_dbus_signal_property_changed(dbus->conn,
entry->path, RIL_CELL_DBUS_INTERFACE,
prop[i].name, DBUS_TYPE_INT32,
G_STRUCT_MEMBER_P(&cell->info, prop[i].off));
mask &= ~prop[i].flag;
}
}
}
static void ril_cell_info_dbus_update_entries(struct ril_cell_info_dbus *dbus,
gboolean emit_signals)
{
GSList *l;
GPtrArray* added = NULL;
GPtrArray* removed = NULL;
/* Remove non-existent cells */
l = dbus->entries;
while (l) {
GSList *next = l->next;
struct ril_cell_entry *entry = l->data;
if (!g_slist_find_custom(dbus->info->cells, &entry->cell,
ril_cell_compare_func)) {
DBG("%s removed", entry->path);
dbus->entries = g_slist_delete_link(dbus->entries, l);
g_dbus_emit_signal(dbus->conn, entry->path,
RIL_CELL_DBUS_INTERFACE,
RIL_CELL_DBUS_REMOVED_SIGNAL,
DBUS_TYPE_INVALID);
g_dbus_unregister_interface(dbus->conn, entry->path,
RIL_CELL_DBUS_INTERFACE);
if (emit_signals) {
if (!removed) {
removed =
g_ptr_array_new_with_free_func(
g_free);
}
/* Steal the path */
g_ptr_array_add(removed, entry->path);
entry->path = NULL;
}
ril_cell_info_destroy_entry(entry);
}
l = next;
}
/* Add new cells */
for (l = dbus->info->cells; l; l = l->next) {
const struct ril_cell *cell = l->data;
struct ril_cell_entry *entry =
ril_cell_info_dbus_find_cell(dbus, cell);
if (entry) {
if (emit_signals) {
int diff = ril_cell_info_dbus_compare(cell,
&entry->cell);
entry->cell = *cell;
ril_cell_info_dbus_property_changed(dbus,
entry, diff);
} else {
entry->cell = *cell;
}
} else {
entry = g_new0(struct ril_cell_entry, 1);
entry->cell = *cell;
entry->cell_id = ril_cell_info_dbus_next_cell_id(dbus);
entry->path = g_strdup_printf("%s/cell_%u", dbus->path,
entry->cell_id);
dbus->entries = g_slist_append(dbus->entries, entry);
DBG("%s added", entry->path);
g_dbus_register_interface(dbus->conn, entry->path,
RIL_CELL_DBUS_INTERFACE,
ril_cell_info_dbus_cell_methods,
ril_cell_info_dbus_cell_signals, NULL,
entry, NULL);
if (emit_signals) {
if (!added) {
added = g_ptr_array_new();
}
g_ptr_array_add(added, entry->path);
}
}
}
if (removed) {
ril_cell_info_dbus_emit_path_list(dbus,
RIL_CELL_INFO_DBUS_CELLS_REMOVED_SIGNAL, removed);
g_ptr_array_free(removed, TRUE);
}
if (added) {
ril_cell_info_dbus_emit_path_list(dbus,
RIL_CELL_INFO_DBUS_CELLS_ADDED_SIGNAL, added);
g_ptr_array_free(added, TRUE);
}
}
static void ril_cell_info_dbus_cells_changed_cb(struct ril_cell_info *info,
void *arg)
{
DBG("");
ril_cell_info_dbus_update_entries((struct ril_cell_info_dbus *)arg,
TRUE);
}
static DBusMessage *ril_cell_info_dbus_get_cells(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ril_cell_info_dbus *dbus = data;
DBusMessage *reply = dbus_message_new_method_return(msg);
DBusMessageIter it, array;
GSList *l;
dbus_message_iter_init_append(reply, &it);
dbus_message_iter_open_container(&it, DBUS_TYPE_ARRAY, "o", &array);
for (l = dbus->entries; l; l = l->next) {
const struct ril_cell_entry *entry = l->data;
dbus_message_iter_append_basic(&array, DBUS_TYPE_OBJECT_PATH,
&entry->path);
}
dbus_message_iter_close_container(&it, &array);
return reply;
}
static const GDBusMethodTable ril_cell_info_dbus_methods[] = {
{ GDBUS_METHOD("GetCells", NULL,
GDBUS_ARGS({ "paths", "ao" }),
ril_cell_info_dbus_get_cells) },
{ }
};
static const GDBusSignalTable ril_cell_info_dbus_signals[] = {
{ GDBUS_SIGNAL(RIL_CELL_INFO_DBUS_CELLS_ADDED_SIGNAL,
GDBUS_ARGS({ "paths", "ao" })) },
{ GDBUS_SIGNAL(RIL_CELL_INFO_DBUS_CELLS_REMOVED_SIGNAL,
GDBUS_ARGS({ "paths", "ao" })) },
{ }
};
struct ril_cell_info_dbus *ril_cell_info_dbus_new(struct ril_modem *md,
struct ril_cell_info *info)
{
struct ril_cell_info_dbus *dbus = g_new0(struct ril_cell_info_dbus, 1);
DBG("%s", ril_modem_get_path(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);
dbus->handler_id = ril_cell_info_add_cells_changed_handler(info,
ril_cell_info_dbus_cells_changed_cb, dbus);
/* Register D-Bus interface */
if (g_dbus_register_interface(dbus->conn, dbus->path,
RIL_CELL_INFO_DBUS_INTERFACE, ril_cell_info_dbus_methods,
ril_cell_info_dbus_signals, NULL, dbus, NULL)) {
ofono_modem_add_interface(md->ofono,
RIL_CELL_INFO_DBUS_INTERFACE);
ril_cell_info_dbus_update_entries(dbus, FALSE);
return dbus;
} else {
ofono_error("RIL D-Bus register failed");
ril_cell_info_dbus_free(dbus);
return NULL;
}
}
void ril_cell_info_dbus_free(struct ril_cell_info_dbus *dbus)
{
if (dbus) {
GSList *l;
DBG("%s", dbus->path);
g_dbus_unregister_interface(dbus->conn, dbus->path,
RIL_CELL_INFO_DBUS_INTERFACE);
/* Unregister cells */
l = dbus->entries;
while (l) {
struct ril_cell_entry *entry = l->data;
g_dbus_unregister_interface(dbus->conn, entry->path,
RIL_CELL_DBUS_INTERFACE);
ril_cell_info_destroy_entry(entry);
l = l->next;
}
g_slist_free(dbus->entries);
dbus_connection_unref(dbus->conn);
ril_cell_info_remove_handler(dbus->info, dbus->handler_id);
ril_cell_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

@@ -14,6 +14,7 @@
*/
#include "ril_config.h"
#include "ril_util.h"
#include "ril_log.h"
#include <gutil_intarray.h>
@@ -190,12 +191,10 @@ GUtilInts *ril_config_get_ints(GKeyFile *file, const char *group,
GUtilIntArray *array = gutil_int_array_new();
while (*ptr) {
const char *str = *ptr++;
char *end = NULL;
long ival = strtol(str, &end, 0);
int val;
if (str[0] && !end[0]) {
gutil_int_array_append(array, ival);
if (ril_parse_int(*ptr++, 0, &val)) {
gutil_int_array_append(array, val);
}
}

View File

@@ -395,45 +395,7 @@ enum ril_cell_info_type {
RIL_CELL_INFO_TYPE_TD_SCDMA = 5
};
struct ril_cell_info_gsm {
int mcc; /* Mobile Country Code (0..999) */
int mnc; /* Mobile Network Code (0..999) */
int lac; /* Location Area Code (0..65535) */
int cid; /* GSM Cell Identity (0..65535) TS 27.007 */
int arfcn; /* 16-bit GSM Absolute RF channel number */
int bsic; /* 6-bit Base Station Identity Code */
int signalStrength; /* (0-31, 99) TS 27.007 */
int bitErrorRate; /* (0-7, 99) TS 27.007 */
int timingAdvance; /* Timing Advance. 1 period = 48/13 us */
};
struct ril_cell_info_wcdma {
int mcc; /* Mobile Country Code (0..999) */
int mnc; /* Mobile Network Code (0..999) */
int lac; /* Location Area Code (0..65535) */
int cid; /* UMTS Cell Identity (0..268435455) TS 25.331 */
int psc; /* Primary Scrambling Code (0..511) TS 25.331) */
int uarfcn; /* 16-bit UMTS Absolute RF Channel Number */
int signalStrength; /* (0-31, 99) TS 27.007 */
int bitErrorRate; /* (0-7, 99) TS 27.007 */
};
struct ril_cell_info_lte {
int mcc; /* Mobile Country Code (0..999) */
int mnc; /* Mobile Network Code (0..999) */
int ci; /* Cell Identity */
int pci; /* Physical cell id (0..503) */
int tac; /* Tracking area code */
int earfcn; /* 18-bit LTE Absolute RC Channel Number */
int signalStrength; /* (0-31, 99) TS 27.007 8.5 */
int rsrp; /* Reference Signal Receive Power TS 36.133 */
int rsrq; /* Reference Signal Receive Quality TS 36.133 */
int rssnr; /* Reference Signal-to-Noise Ratio TS 36.101*/
int cqi; /* Channel Quality Indicator TS 36.101 */
int timingAdvance; /* (Distance = 300m/us) TS 36.321 */
};
/* RIL Request Messages */
/* RIL Request Messages, ofono -> rild */
#define RIL_REQUEST_GET_SIM_STATUS 1
#define RIL_REQUEST_ENTER_SIM_PIN 2
#define RIL_REQUEST_ENTER_SIM_PUK 3
@@ -568,7 +530,7 @@ struct ril_cell_info_lte {
#define RIL_REQUEST_GET_RADIO_CAPABILITY 130
#define RIL_REQUEST_SET_RADIO_CAPABILITY 131
/* RIL Unsolicited Messages */
/* RIL Unsolicited Messages, rild -> ofono */
#define RIL_UNSOL_RESPONSE_BASE 1000
#define RIL_UNSOL_RESPONSE_RADIO_STATE_CHANGED 1000
#define RIL_UNSOL_RESPONSE_CALL_STATE_CHANGED 1001
@@ -616,6 +578,9 @@ struct ril_cell_info_lte {
#define RIL_UNSOL_ON_SS 1043
#define RIL_UNSOL_STK_CC_ALPHA_NOTIFY 1044
/* A special request, ofono -> rild */
#define RIL_RESPONSE_ACKNOWLEDGEMENT 800
/* Suplementary services Service class*/
#define SERVICE_CLASS_NONE 0

View File

@@ -94,6 +94,7 @@ struct ril_data_priv {
struct ril_network *network;
struct ril_data_manager *dm;
enum ril_data_priv_flags flags;
struct ril_vendor_hook *vendor_hook;
struct ril_data_request *req_queue;
struct ril_data_request *pending_req;
@@ -535,11 +536,18 @@ static void ril_data_query_data_calls_cb(GRilIoChannel *io, int ril_status,
struct ril_data *self = RIL_DATA(user_data);
struct ril_data_priv *priv = self->priv;
/*
* Only RIL_E_SUCCESS and RIL_E_RADIO_NOT_AVAILABLE are expected here,
* all other errors are filtered out by ril_voicecall_clcc_retry()
*/
GASSERT(priv->query_id);
priv->query_id = 0;
if (ril_status == RIL_E_SUCCESS) {
ril_data_set_calls(self, ril_data_call_list_parse(data, len,
priv->options.data_call_format));
} else {
/* RADIO_NOT_AVAILABLE == no calls */
ril_data_set_calls(self, NULL);
}
}
@@ -1162,6 +1170,18 @@ struct ril_data *ril_data_new(struct ril_data_manager *dm, const char *name,
return NULL;
}
static gboolean ril_data_poll_call_state_retry(GRilIoRequest* req,
int ril_status, const void* resp_data, guint resp_len, void* user_data)
{
switch (ril_status) {
case RIL_E_SUCCESS:
case RIL_E_RADIO_NOT_AVAILABLE:
return FALSE;
default:
return TRUE;
}
}
void ril_data_poll_call_state(struct ril_data *self)
{
if (G_LIKELY(self)) {
@@ -1171,6 +1191,8 @@ void ril_data_poll_call_state(struct ril_data *self)
GRilIoRequest *req = grilio_request_new();
grilio_request_set_retry(req, RIL_RETRY_SECS*1000, -1);
grilio_request_set_retry_func(req,
ril_data_poll_call_state_retry);
priv->query_id =
grilio_queue_send_request_full(priv->q, req,
RIL_REQUEST_DATA_CALL_LIST,
@@ -1227,8 +1249,7 @@ static void ril_data_power_update(struct ril_data *self)
{
struct ril_data_priv *priv = self->priv;
if (priv->pending_req || priv->req_queue ||
(priv->flags & RIL_DATA_FLAG_ALLOWED)) {
if (priv->pending_req || priv->req_queue) {
ril_radio_power_on(priv->radio, self);
} else {
ril_radio_power_off(priv->radio, self);

View File

@@ -23,6 +23,7 @@
#include <arpa/inet.h>
#include "ofono.h"
#include "common.h"
#include "mtu-watch.h"
@@ -63,7 +64,7 @@ static char *ril_gprs_context_netmask(const char *bits)
const char* str;
struct in_addr in;
in.s_addr = htonl((nbits == 32) ? 0xffffffff :
((1 << nbits)-1) << (32-nbits));
((1u << nbits)-1) << (32-nbits));
str = inet_ntoa(in);
if (str) {
return g_strdup(str);
@@ -444,7 +445,7 @@ static void ril_gprs_context_activate_primary(struct ofono_gprs_context *gc,
/* Let's make sure that we aren't connecting when roaming not allowed */
if (rs == NETWORK_REGISTRATION_STATUS_ROAMING) {
struct ofono_gprs *gprs = ril_modem_ofono_gprs(gcd->modem);
if (!ofono_gprs_get_roaming_allowed(gprs) &&
if (!__ofono_gprs_get_roaming_allowed(gprs) &&
ril_netreg_check_if_really_roaming(netreg, rs) ==
NETWORK_REGISTRATION_STATUS_ROAMING) {
struct ofono_error error;

View File

@@ -308,16 +308,18 @@ static void ril_modem_set_online(struct ofono_modem *modem, ofono_bool_t online,
ofono_modem_online_cb_t cb, void *data)
{
struct ril_modem_data *md = ril_modem_data_from_ofono(modem);
struct ril_radio *radio = md->modem.radio;
struct ril_modem_online_request *req;
DBG("%s going %sline", ofono_modem_get_path(modem),
online ? "on" : "off");
ril_radio_set_online(radio, online);
if (online) {
ril_radio_power_on(md->modem.radio, RADIO_POWER_TAG(md));
ril_radio_power_on(radio, RADIO_POWER_TAG(md));
req = &md->set_online;
} else {
ril_radio_power_off(md->modem.radio, RADIO_POWER_TAG(md));
ril_radio_power_off(radio, RADIO_POWER_TAG(md));
req = &md->set_offline;
}
@@ -390,8 +392,8 @@ static void ril_modem_remove(struct ofono_modem *ofono)
ril_network_unref(modem->network);
ril_sim_card_unref(modem->sim_card);
ril_cell_info_unref(modem->cell_info);
ril_data_unref(modem->data);
sailfish_cell_info_unref(modem->cell_info);
grilio_channel_unref(modem->io);
grilio_queue_cancel_all(md->q, FALSE);
grilio_queue_unref(md->q);
@@ -408,7 +410,7 @@ struct ril_modem *ril_modem_create(GRilIoChannel *io, const char *log_prefix,
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)
struct sailfish_cell_info *cell_info)
{
/* Skip the slash from the path, it looks like "/ril_0" */
struct ofono_modem *ofono = ofono_modem_create(path + 1,
@@ -438,7 +440,7 @@ struct ril_modem *ril_modem_create(GRilIoChannel *io, const char *log_prefix,
modem->network = ril_network_ref(network);
modem->sim_card = ril_sim_card_ref(card);
modem->sim_settings = ril_sim_settings_ref(settings);
modem->cell_info = ril_cell_info_ref(cell_info);
modem->cell_info = sailfish_cell_info_ref(cell_info);
modem->data = ril_data_ref(data);
modem->io = grilio_channel_ref(io);
md->q = grilio_queue_new(io);

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
@@ -14,15 +14,16 @@
*/
#include "ril_plugin.h"
#include "ril_cell_info.h"
#include "ril_util.h"
#include "ril_log.h"
#include <sailfish_cell_info.h>
#include "ofono.h"
struct ril_netmon {
struct ofono_netmon *netmon;
struct ril_cell_info *cell_info;
struct sailfish_cell_info *cell_info;
guint register_id;
};
@@ -39,7 +40,7 @@ static void ril_netmon_format_mccmnc(char *s_mcc, char *s_mnc, int mcc, int mnc)
if (mcc >= 0 && mcc <= 999) {
snprintf(s_mcc, OFONO_MAX_MCC_LENGTH + 1, "%03d", mcc);
if (mnc >= 0 && mnc <= 999) {
const int mnclen = mnclength(mcc, mnc);
const unsigned int mnclen = mnclength(mcc, mnc);
const char *format[] = { "%d", "%02d", "%03d" };
const char *fmt = (mnclen > 0 &&
mnclen <= G_N_ELEMENTS(format)) ?
@@ -50,7 +51,7 @@ static void ril_netmon_format_mccmnc(char *s_mcc, char *s_mnc, int mcc, int mnc)
}
static void ril_netmon_notify_gsm(struct ofono_netmon *netmon,
const struct ril_cell_info_gsm *gsm)
const struct sailfish_cell_info_gsm *gsm)
{
char mcc[OFONO_MAX_MCC_LENGTH + 1];
char mnc[OFONO_MAX_MNC_LENGTH + 1];
@@ -68,7 +69,7 @@ static void ril_netmon_notify_gsm(struct ofono_netmon *netmon,
}
static void ril_netmon_notify_wcdma(struct ofono_netmon *netmon,
const struct ril_cell_info_wcdma *wcdma)
const struct sailfish_cell_info_wcdma *wcdma)
{
char mcc[OFONO_MAX_MCC_LENGTH + 1];
char mnc[OFONO_MAX_MNC_LENGTH + 1];
@@ -87,7 +88,7 @@ static void ril_netmon_notify_wcdma(struct ofono_netmon *netmon,
}
static void ril_netmon_notify_lte(struct ofono_netmon *netmon,
const struct ril_cell_info_lte *lte)
const struct sailfish_cell_info_lte *lte)
{
char mcc[OFONO_MAX_MCC_LENGTH + 1];
char mnc[OFONO_MAX_MNC_LENGTH + 1];
@@ -111,19 +112,19 @@ static void ril_netmon_request_update(struct ofono_netmon *netmon,
GSList *l;
for (l = nm->cell_info->cells; l; l = l->next) {
const struct ril_cell *cell = l->data;
const struct sailfish_cell *cell = l->data;
if (cell->registered) {
switch (cell->type) {
case RIL_CELL_INFO_TYPE_GSM:
case SAILFISH_CELL_TYPE_GSM:
ril_netmon_notify_gsm(netmon,
&cell->info.gsm);
break;
case RIL_CELL_INFO_TYPE_WCDMA:
case SAILFISH_CELL_TYPE_WCDMA:
ril_netmon_notify_wcdma(netmon,
&cell->info.wcdma);
break;
case RIL_CELL_INFO_TYPE_LTE:
case SAILFISH_CELL_TYPE_LTE:
ril_netmon_notify_lte(netmon,
&cell->info.lte);
break;
@@ -156,7 +157,7 @@ static int ril_netmon_probe(struct ofono_netmon *netmon, unsigned int vendor,
if (modem->cell_info) {
struct ril_netmon *nm = g_slice_new0(struct ril_netmon);
nm->cell_info = ril_cell_info_ref(modem->cell_info);
nm->cell_info = sailfish_cell_info_ref(modem->cell_info);
nm->netmon = netmon;
ofono_netmon_set_data(netmon, nm);
@@ -182,7 +183,7 @@ static void ril_netmon_remove(struct ofono_netmon *netmon)
g_source_remove(nm->register_id);
}
ril_cell_info_unref(nm->cell_info);
sailfish_cell_info_unref(nm->cell_info);
g_slice_free(struct ril_netmon, nm);
}

View File

@@ -499,7 +499,7 @@ static int ril_netreg_probe(struct ofono_netreg *netreg, unsigned int vendor,
static void ril_netreg_remove(struct ofono_netreg *netreg)
{
struct ril_netreg *nd = ril_netreg_get_data(netreg);
int i;
unsigned int i;
DBG("%p", netreg);
grilio_queue_cancel_all(nd->q, FALSE);

View File

@@ -20,8 +20,6 @@
#include "ril_util.h"
#include "ril_log.h"
#include "sailfish_watch.h"
#include <grilio_queue.h>
#include <grilio_request.h>
#include <grilio_parser.h>
@@ -43,11 +41,6 @@ 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,
@@ -65,8 +58,6 @@ struct ril_network_priv {
GRilIoQueue *q;
struct ril_radio *radio;
struct ril_sim_card *sim_card;
struct sailfish_watch *watch;
gulong watch_event_id[WATCH_EVENT_COUNT];
int rat;
char *log_prefix;
guint operator_poll_id;
@@ -217,8 +208,14 @@ static gboolean ril_network_parse_response(struct ril_network *self,
reg->max_calls = 2;
}
reg->lac = slac ? strtol(slac, NULL, 16) : -1;
reg->ci = sci ? strtol(sci, NULL, 16) : -1;
if (!ril_parse_int(slac, 16, &reg->lac)) {
reg->lac = -1;
}
if (!ril_parse_int(sci, 16, &reg->ci)) {
reg->ci = -1;
}
reg->access_tech = ril_parse_tech(stech, &reg->ril_tech);
DBG_(self, "%s,%s,%s,%d,%s,%s,%s",
@@ -487,7 +484,7 @@ static gboolean ril_network_can_set_pref_mode(struct ril_network *self)
{
struct ril_network_priv *priv = self->priv;
return priv->watch->online && ril_sim_card_ready(priv->sim_card);
return priv->radio->online && ril_sim_card_ready(priv->sim_card);
}
static gboolean ril_network_set_rat_holdoff_cb(gpointer user_data)
@@ -762,7 +759,7 @@ static void ril_network_radio_state_cb(struct ril_radio *radio, void *data)
}
}
static void ril_network_online_cb(struct sailfish_watch *watch, void *data)
static void ril_network_radio_online_cb(struct ril_radio *radio, void *data)
{
struct ril_network *self = RIL_NETWORK(data);
@@ -825,7 +822,6 @@ struct ril_network *ril_network_new(const char *path, GRilIoChannel *io,
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]) ?
@@ -842,9 +838,9 @@ struct ril_network *ril_network_new(const char *path, GRilIoChannel *io,
priv->radio_event_id[RADIO_EVENT_STATE_CHANGED] =
ril_radio_add_state_changed_handler(priv->radio,
ril_network_radio_state_cb, self);
priv->watch_event_id[WATCH_EVENT_ONLINE] =
sailfish_watch_add_modem_changed_handler(priv->watch,
ril_network_online_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->settings_event_id =
ril_sim_settings_add_pref_mode_changed_handler(settings,
ril_network_pref_mode_changed_cb, self);
@@ -901,6 +897,7 @@ static void ril_network_finalize(GObject *object)
enum ril_network_timer tid;
DBG_(self, "");
for (tid=0; tid<TIMER_COUNT; tid++) {
ril_network_stop_timer(self, tid);
}
@@ -911,8 +908,6 @@ static void ril_network_finalize(GObject *object)
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,

File diff suppressed because it is too large Load Diff

View File

@@ -51,12 +51,12 @@ struct ril_modem {
const char *log_prefix;
const char *ecclist_file;
struct ofono_modem *ofono;
struct sailfish_cell_info *cell_info;
struct ril_radio *radio;
struct ril_data *data;
struct ril_network *network;
struct ril_sim_card *sim_card;
struct ril_sim_settings *sim_settings;
struct ril_cell_info *cell_info;
struct ril_slot_config config;
};
@@ -65,18 +65,13 @@ 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_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_modem *ril_modem_create(GRilIoChannel *io, const char *log_prefix,
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);
struct sailfish_cell_info *cell_info);
void ril_modem_delete(struct ril_modem *modem);
struct ofono_sim *ril_modem_ofono_sim(struct ril_modem *modem);
struct ofono_gprs *ril_modem_ofono_gprs(struct ril_modem *modem);

View File

@@ -50,12 +50,14 @@ 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 };
@@ -75,7 +77,8 @@ static inline gboolean ril_radio_power_should_be_on(struct ril_radio *self)
{
struct ril_radio_priv *priv = self->priv;
return !priv->power_cycle && g_hash_table_size(priv->req_table) > 0;
return self->online && !priv->power_cycle &&
g_hash_table_size(priv->req_table) > 0;
}
static inline gboolean ril_radio_state_off(enum ril_radio_state radio_state)
@@ -280,6 +283,19 @@ 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)
{
@@ -287,6 +303,13 @@ 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)) {
@@ -424,6 +447,7 @@ 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

@@ -22,6 +22,7 @@ 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);
@@ -34,8 +35,11 @@ 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);

View File

@@ -221,8 +221,7 @@ static void ril_sim_card_subscribe(struct ril_sim_card *self, int app_index,
static int ril_sim_card_select_app(const struct ril_sim_card_status *status)
{
int selected_app = -1;
guint i;
int i, selected_app = -1;
for (i = 0; i < status->num_apps; i++) {
const int type = status->apps[i].app_type;

View File

@@ -1,53 +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.
*/
#ifndef RIL_SIM_INFO_H
#define RIL_SIM_INFO_H
#include "ril_types.h"
struct ril_sim_info {
GObject object;
struct ril_sim_info_priv *priv;
const char *iccid;
const char *imsi;
const char *spn;
};
struct ofono_sim;
typedef void (*ril_sim_info_cb_t)(struct ril_sim_info *info, void *arg);
struct ril_sim_info *ril_sim_info_new(const char *log_prefix);
struct ril_sim_info *ril_sim_info_ref(struct ril_sim_info *info);
void ril_sim_info_unref(struct ril_sim_info *si);
void ril_sim_info_set_ofono_sim(struct ril_sim_info *si, struct ofono_sim *sim);
void ril_sim_info_set_network(struct ril_sim_info *si, struct ril_network *net);
gulong ril_sim_info_add_iccid_changed_handler(struct ril_sim_info *si,
ril_sim_info_cb_t cb, void *arg);
gulong ril_sim_info_add_imsi_changed_handler(struct ril_sim_info *si,
ril_sim_info_cb_t cb, void *arg);
gulong ril_sim_info_add_spn_changed_handler(struct ril_sim_info *si,
ril_sim_info_cb_t cb, void *arg);
void ril_sim_info_remove_handler(struct ril_sim_info *si, gulong id);
#endif /* RIL_SIM_INFO_H */
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -20,9 +20,6 @@
#include <gutil_misc.h>
#include "ofono.h"
#include "storage.h"
#define RIL_PREF_MODE_DEFAULT(self) (\
((self)->techs & OFONO_RADIO_ACCESS_MODE_LTE) ? \
OFONO_RADIO_ACCESS_MODE_LTE : \
@@ -41,7 +38,6 @@ enum sailfish_watch_events {
struct ril_sim_settings_priv {
gulong watch_event_id[WATCH_EVENT_COUNT];
struct sailfish_watch *watch;
GKeyFile *storage;
char *imsi;
};

View File

@@ -34,8 +34,6 @@ 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,
struct ofono_sim *sim);
void ril_sim_settings_set_pref_mode(struct ril_sim_settings *s,
enum ofono_radio_access_mode mode);
gulong ril_sim_settings_add_imsi_changed_handler(struct ril_sim_settings *s,

View File

@@ -471,7 +471,7 @@ static int ril_sms_probe(struct ofono_sms *sms, unsigned int vendor,
static void ril_sms_remove(struct ofono_sms *sms)
{
int i;
unsigned int i;
struct ril_sms *sd = ril_sms_get_data(sms);
DBG("");

View File

@@ -268,7 +268,7 @@ static int ril_stk_probe(struct ofono_stk *stk, unsigned int vendor, void *data)
static void ril_stk_remove(struct ofono_stk *stk)
{
struct ril_stk *sd = ril_stk_get_data(stk);
int i;
unsigned int i;
DBG("");
ofono_stk_set_data(stk, NULL);

View File

@@ -21,6 +21,13 @@
#
#EmptyConfig=false
# User and group for the ofono process. RIL clients are typically
# expected to run under radio:radio.
#
# Default is radio:radio
#
#Identity=radio:radio
# If the phone has more than one SIM slot, the 3G/LTE module may be
# shared by all modems, meaning that only one of the slots can use
# 3G/LTE. In order to "hand 4G over" to the other slot, the modem
@@ -176,3 +183,22 @@ socket=/dev/socket/rild
# Default true
#
#enableVoicecall=true
# Timeout for the modem to show up, in milliseconds. Those that don't
# show up before this timeout expires, will be dropped (ignored).
#
# In some fairly rare cases it makes sense to shorten this timeout for
# optional modems (which may or may not be available), to speed up the
# boot up process.
#
# The default is 20000 (20 seconds)
#
#startTimeout=20000
# This allows to use deprecated RIL_REQUEST_GET_IMEI instead of
# RIL_REQUEST_DEVICE_IDENTITY to query IMEI from the modem. Some
# RILs (e.g. MTK) still don't understand RIL_REQUEST_DEVICE_IDENTITY.
#
# Default is false (use RIL_REQUEST_DEVICE_IDENTITY)
#
#legacyImeiQuery=false

View File

@@ -45,7 +45,6 @@ struct ril_modem;
struct ril_radio;
struct ril_network;
struct ril_sim_card;
struct ril_cell_info;
struct ril_slot_config {
guint slot;

View File

@@ -20,6 +20,8 @@
#include <sys/socket.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include "common.h"
#include "netreg.h"
@@ -233,6 +235,8 @@ const char *ril_request_to_string(guint request)
RIL_REQUEST_(SHUTDOWN);
RIL_REQUEST_(GET_RADIO_CAPABILITY);
RIL_REQUEST_(SET_RADIO_CAPABILITY);
case RIL_RESPONSE_ACKNOWLEDGEMENT:
return "RESPONSE_ACK";
default:
snprintf(unknown, sizeof(unknown), "RIL_REQUEST_%d", request);
return unknown;
@@ -322,8 +326,7 @@ int ril_parse_tech(const char *stech, int *ril_tech)
{
int access_tech = -1;
int tech = -1;
if (stech && stech[0]) {
tech = atoi(stech);
if (ril_parse_int(stech, 0, &tech)) {
switch (tech) {
case RADIO_TECH_GPRS:
case RADIO_TECH_GSM:
@@ -380,8 +383,8 @@ gboolean ril_parse_mcc_mnc(const char *str, struct ofono_network_operator *op)
if (i == OFONO_MAX_MCC_LENGTH) {
/* Usually 2 but sometimes 3 digit network code */
for (i=0;
i<OFONO_MAX_MNC_LENGTH && *ptr && isdigit(*ptr);
for (i = 0;
i < OFONO_MAX_MNC_LENGTH && *ptr && isdigit(*ptr);
i++) {
op->mnc[i] = *ptr++;
}
@@ -408,6 +411,26 @@ gboolean ril_parse_mcc_mnc(const char *str, struct ofono_network_operator *op)
return FALSE;
}
gboolean ril_parse_int(const char *str, int base, int *value)
{
gboolean ok = FALSE;
if (str && str[0]) {
char *str2 = g_strstrip(g_strdup(str));
char *end = str2;
long l;
errno = 0;
l = strtol(str2, &end, base);
ok = !*end && errno != ERANGE && l >= INT_MIN && l <= INT_MAX;
if (ok && value) {
*value = (int)l;
}
g_free(str2);
}
return ok;
}
/*
* 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
@@ -26,6 +26,7 @@ const char *ril_unsol_event_to_string(guint event);
const char *ril_radio_state_to_string(int radio_state);
int ril_parse_tech(const char *stech, int *ril_tech);
gboolean ril_parse_mcc_mnc(const char *str, struct ofono_network_operator *op);
gboolean ril_parse_int(const char *str, int base, int *value);
#define ril_error_init_ok(err) \
((err)->error = 0, (err)->type = OFONO_ERROR_TYPE_NO_ERROR)

View File

@@ -306,7 +306,8 @@ static void ril_voicecall_lastcause_cb(GRilIoChannel *io, int status,
case CALL_FAIL_ERROR_UNSPECIFIED:
call_status = ril_voicecall_status_with_id(vc, id);
if (call_status == CALL_STATUS_DIALING ||
call_status == CALL_STATUS_ALERTING) {
call_status == CALL_STATUS_ALERTING ||
call_status == CALL_STATUS_INCOMING) {
reason = OFONO_DISCONNECT_REASON_REMOTE_HANGUP;
}
break;
@@ -334,14 +335,18 @@ static void ril_voicecall_clcc_poll_cb(GRilIoChannel *io, int status,
GASSERT(vd->clcc_poll_id);
vd->clcc_poll_id = 0;
if (status != RIL_E_SUCCESS) {
ofono_error("We are polling CLCC and received an error");
ofono_error("All bets are off for call management");
return;
/*
* Only RIL_E_SUCCESS and RIL_E_RADIO_NOT_AVAILABLE are expected here,
* all other errors are filtered out by ril_voicecall_clcc_retry()
*/
if (status == RIL_E_SUCCESS) {
calls = ril_voicecall_parse_clcc(data, len);
} else {
/* RADIO_NOT_AVAILABLE == no calls */
GASSERT(status == RIL_E_RADIO_NOT_AVAILABLE);
calls = NULL;
}
calls = ril_voicecall_parse_clcc(data, len);
n = calls;
o = vd->calls;
@@ -435,12 +440,25 @@ static void ril_voicecall_clcc_poll_cb(GRilIoChannel *io, int status,
vd->calls = calls;
}
static gboolean ril_voicecall_clcc_retry(GRilIoRequest* req, int ril_status,
const void* response_data, guint response_len, void* user_data)
{
switch (ril_status) {
case RIL_E_SUCCESS:
case RIL_E_RADIO_NOT_AVAILABLE:
return FALSE;
default:
return TRUE;
}
}
static void ril_voicecall_clcc_poll(struct ril_voicecall *vd)
{
GASSERT(vd);
if (!vd->clcc_poll_id) {
GRilIoRequest* req = grilio_request_new();
grilio_request_set_retry(req, RIL_RETRY_MS, -1);
grilio_request_set_retry_func(req, ril_voicecall_clcc_retry);
vd->clcc_poll_id = grilio_queue_send_request_full(vd->q,
req, RIL_REQUEST_GET_CURRENT_CALLS,
ril_voicecall_clcc_poll_cb, NULL, vd);

View File

@@ -0,0 +1,158 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 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 <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <glib.h>
#include <ofono/modem.h>
#include <ofono/gprs-context.h>
#include <ofono/log.h>
#include <ofono/lte.h>
#include <gril/gril.h>
#include <gril/grilutil.h>
#include "rilmodem.h"
struct ril_lte_data {
GRil *ril;
};
static void ril_lte_set_default_attach_info_cb(struct ril_msg *message,
gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_lte_cb_t cb = cbd->cb;
struct ofono_lte *lte = cbd->user;
struct ril_lte_data *ld = ofono_lte_get_data(lte);
DBG("");
if (message->error == RIL_E_SUCCESS) {
g_ril_print_response_no_args(ld->ril, message);
CALLBACK_WITH_SUCCESS(cb, cbd->data);
} else {
ofono_error("%s: RIL error %s", __func__,
ril_error_to_string(message->error));
CALLBACK_WITH_FAILURE(cb, cbd->data);
}
}
static void ril_lte_set_default_attach_info(const struct ofono_lte *lte,
const struct ofono_lte_default_attach_info *info,
ofono_lte_cb_t cb, void *data)
{
struct ril_lte_data *ld = ofono_lte_get_data(lte);
struct cb_data *cbd = cb_data_new(cb, data, (struct ofono_lte *)lte);
struct parcel rilp;
char buf[OFONO_GPRS_MAX_APN_LENGTH + 1];
DBG("%s", info->apn);
parcel_init(&rilp);
parcel_w_int32(&rilp, 5);
if (strlen(info->apn) > 0) {
sprintf(buf, "%s", info->apn);
parcel_w_string(&rilp, buf);
} else
parcel_w_string(&rilp, ""); /* apn */
parcel_w_string(&rilp, "ip"); /* protocol */
parcel_w_int32(&rilp, 0); /* auth type */
parcel_w_string(&rilp, ""); /* username */
parcel_w_string(&rilp, ""); /* password */
if (g_ril_send(ld->ril, RIL_REQUEST_SET_INITIAL_ATTACH_APN, &rilp,
ril_lte_set_default_attach_info_cb, cbd, g_free) > 0)
return;
g_free(cbd);
CALLBACK_WITH_FAILURE(cb, data);
}
static gboolean lte_delayed_register(gpointer user_data)
{
struct ofono_lte *lte = user_data;
DBG("");
ofono_lte_register(lte);
return FALSE;
}
static int ril_lte_probe(struct ofono_lte *lte, void *user_data)
{
GRil *ril = user_data;
struct ril_lte_data *ld;
DBG("");
ld = g_try_new0(struct ril_lte_data, 1);
if (ld == NULL)
return -ENOMEM;
ld->ril = g_ril_clone(ril);
ofono_lte_set_data(lte, ld);
g_idle_add(lte_delayed_register, lte);
return 0;
}
static void ril_lte_remove(struct ofono_lte *lte)
{
struct ril_lte_data *ld = ofono_lte_get_data(lte);
DBG("");
ofono_lte_set_data(lte, NULL);
g_ril_unref(ld->ril);
g_free(ld);
}
static struct ofono_lte_driver driver = {
.name = RILMODEM,
.probe = ril_lte_probe,
.remove = ril_lte_remove,
.set_default_attach_info = ril_lte_set_default_attach_info,
};
void ril_lte_init(void)
{
ofono_lte_driver_register(&driver);
}
void ril_lte_exit(void)
{
ofono_lte_driver_unregister(&driver);
}

View File

@@ -111,7 +111,7 @@ static void ril_set_rat_mode(struct ofono_radio_settings *rs,
struct radio_data *rd = ofono_radio_settings_get_data(rs);
struct cb_data *cbd = cb_data_new(cb, data, rs);
struct parcel rilp;
int pref = PREF_NET_TYPE_GSM_WCDMA;
int pref = PREF_NET_TYPE_LTE_GSM_WCDMA;
switch (mode) {
case OFONO_RADIO_ACCESS_MODE_ANY:

View File

@@ -54,6 +54,7 @@ static int rilmodem_init(void)
ril_netmon_init();
ril_stk_init();
ril_cbs_init();
ril_lte_init();
return 0;
}
@@ -78,6 +79,7 @@ static void rilmodem_exit(void)
ril_netmon_exit();
ril_stk_exit();
ril_cbs_exit();
ril_lte_exit();
}
OFONO_PLUGIN_DEFINE(rilmodem, "RIL modem driver", VERSION,

View File

@@ -78,3 +78,6 @@ extern void ril_stk_exit(void);
extern void ril_cbs_init(void);
extern void ril_cbs_exit(void);
extern void ril_lte_init(void);
extern void ril_lte_exit(void);

View File

@@ -183,6 +183,24 @@ static void ril_stk_session_end_notify(struct ril_msg *message,
ofono_stk_proactive_session_end_notify(stk);
}
static void ril_stk_initialize_cb(struct ril_msg *message,
gpointer user_data)
{
struct ofono_stk *stk = user_data;
struct stk_data *sd = ofono_stk_get_data(stk);
if (message->error != RIL_E_SUCCESS) {
ofono_error("%s RILD reply failure: %s",
g_ril_request_id_to_string(sd->ril, message->req),
ril_error_to_string(message->error));
ofono_stk_remove(stk);
return;
}
ofono_stk_register(stk);
}
static int ril_stk_probe(struct ofono_stk *stk, unsigned int vendor,
void *user)
{
@@ -204,7 +222,8 @@ static int ril_stk_probe(struct ofono_stk *stk, unsigned int vendor,
g_ril_register(ril, RIL_UNSOL_STK_EVENT_NOTIFY,
ril_stk_event_notify, stk);
ofono_stk_register(stk);
g_ril_send(data->ril, RIL_REQUEST_REPORT_STK_SERVICE_IS_RUNNING, NULL,
ril_stk_initialize_cb, stk, NULL);
return 0;
}

View File

@@ -0,0 +1,482 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 Piotr Haber. 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
#define _GNU_SOURCE
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/stat.h>
#include <glib.h>
#include <ofono/log.h>
#include <ofono/modem.h>
#include <ofono/gprs-context.h>
#include "gatchat.h"
#include "gatresult.h"
#include "telitmodem.h"
static const char *none_prefix[] = { NULL };
static const char *cgpaddr_prefix[] = { "+CGPADDR:", NULL };
static const char *cgcontrdp_prefix[] = { "+CGCONTRDP:", NULL };
enum state {
STATE_IDLE,
STATE_ENABLING,
STATE_DISABLING,
STATE_ACTIVE,
};
enum auth_method {
AUTH_METHOD_NONE,
AUTH_METHOD_PAP,
AUTH_METHOD_CHAP,
};
struct gprs_context_data {
GAtChat *chat;
unsigned int active_context;
char username[OFONO_GPRS_MAX_USERNAME_LENGTH + 1];
char password[OFONO_GPRS_MAX_PASSWORD_LENGTH + 1];
enum auth_method auth_method;
enum state state;
enum ofono_gprs_proto proto;
char address[64];
char netmask[64];
char gateway[64];
char dns1[64];
char dns2[64];
ofono_gprs_context_cb_t cb;
void *cb_data;
};
static void failed_setup(struct ofono_gprs_context *gc,
GAtResult *result, gboolean deactivate)
{
struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
struct ofono_error error;
char buf[64];
DBG("deactivate %d", deactivate);
if (deactivate == TRUE) {
sprintf(buf, "AT+CGACT=0,%u", gcd->active_context);
g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL);
}
gcd->active_context = 0;
gcd->state = STATE_IDLE;
if (result == NULL) {
CALLBACK_WITH_FAILURE(gcd->cb, gcd->cb_data);
return;
}
decode_at_error(&error, g_at_result_final_response(result));
gcd->cb(&error, gcd->cb_data);
}
static void session_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_gprs_context *gc = user_data;
struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
struct ofono_modem *modem;
const char *interface;
const char *dns[3];
DBG("ok %d", ok);
if (!ok) {
ofono_error("Failed to establish session");
failed_setup(gc, result, TRUE);
return;
}
gcd->state = STATE_ACTIVE;
dns[0] = gcd->dns1;
dns[1] = gcd->dns2;
dns[2] = 0;
modem = ofono_gprs_context_get_modem(gc);
interface = ofono_modem_get_string(modem, "NetworkInterface");
ofono_gprs_context_set_interface(gc, interface);
ofono_gprs_context_set_ipv4_address(gc, gcd->address, TRUE);
ofono_gprs_context_set_ipv4_netmask(gc, gcd->netmask);
ofono_gprs_context_set_ipv4_gateway(gc, gcd->gateway);
ofono_gprs_context_set_ipv4_dns_servers(gc, dns);
CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
}
static void contrdp_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_gprs_context *gc = user_data;
struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
char buf[64];
int cid, bearer_id;
const char *apn, *ip_mask, *gw;
const char *dns1, *dns2;
GAtResultIter iter;
gboolean found = FALSE;
DBG("ok %d", ok);
if (!ok) {
ofono_error("Unable to get context dynamic paramerers");
failed_setup(gc, result, TRUE);
return;
}
g_at_result_iter_init(&iter, result);
while (g_at_result_iter_next(&iter, "+CGCONTRDP:")) {
if (!g_at_result_iter_next_number(&iter, &cid))
goto error;
if (!g_at_result_iter_next_number(&iter, &bearer_id))
goto error;
if (!g_at_result_iter_next_string(&iter, &apn))
goto error;
if (!g_at_result_iter_next_string(&iter, &ip_mask))
goto error;
if (!g_at_result_iter_next_string(&iter, &gw))
goto error;
if (!g_at_result_iter_next_string(&iter, &dns1))
goto error;
if (!g_at_result_iter_next_string(&iter, &dns2))
goto error;
if ((unsigned int) cid == gcd->active_context) {
found = TRUE;
if (strcmp(gcd->address, "") != 0)
strncpy(gcd->netmask,
&ip_mask[strlen(gcd->address) + 1],
sizeof(gcd->netmask));
strncpy(gcd->gateway, gw, sizeof(gcd->gateway));
strncpy(gcd->dns1, dns1, sizeof(gcd->dns1));
strncpy(gcd->dns2, dns2, sizeof(gcd->dns2));
}
}
if (found == FALSE)
goto error;
ofono_info("IP: %s", gcd->address);
ofono_info("MASK: %s", gcd->netmask);
ofono_info("GW: %s", gcd->gateway);
ofono_info("DNS: %s, %s", gcd->dns1, gcd->dns2);
sprintf(buf, "AT+CGDATA=\"M-RAW_IP\",%d", gcd->active_context);
if (g_at_chat_send(gcd->chat, buf, none_prefix,
session_cb, gc, NULL) > 0)
return;
error:
failed_setup(gc, NULL, TRUE);
}
static void address_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_gprs_context *gc = user_data;
struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
int cid;
const char *address;
char buf[64];
GAtResultIter iter;
DBG("ok %d", ok);
if (!ok) {
ofono_error("Unable to get context address");
failed_setup(gc, result, TRUE);
return;
}
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CGPADDR:"))
goto error;
if (!g_at_result_iter_next_number(&iter, &cid))
goto error;
if ((unsigned int) cid != gcd->active_context)
goto error;
if (!g_at_result_iter_next_string(&iter, &address))
goto error;
strncpy(gcd->address, address, sizeof(gcd->address));
sprintf(buf, "AT+CGCONTRDP=%d", gcd->active_context);
if (g_at_chat_send(gcd->chat, buf, cgcontrdp_prefix,
contrdp_cb, gc, NULL) > 0)
return;
error:
failed_setup(gc, NULL, TRUE);
}
static void activate_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_gprs_context *gc = user_data;
struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
char buf[64];
DBG("ok %d", ok);
if (!ok) {
ofono_error("Unable to activate context");
failed_setup(gc, result, FALSE);
return;
}
sprintf(buf, "AT+CGPADDR=%u", gcd->active_context);
if (g_at_chat_send(gcd->chat, buf, cgpaddr_prefix,
address_cb, gc, NULL) > 0)
return;
failed_setup(gc, NULL, TRUE);
}
static void setup_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_gprs_context *gc = user_data;
struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
char buf[128];
DBG("ok %d", ok);
if (!ok) {
ofono_error("Failed to setup context");
failed_setup(gc, result, FALSE);
return;
}
if (gcd->username[0] && gcd->password[0])
sprintf(buf, "AT#PDPAUTH=%u,%u,\"%s\",\"%s\"",
gcd->active_context, gcd->auth_method,
gcd->username, gcd->password);
else
sprintf(buf, "AT#PDPAUTH=%u,0", gcd->active_context);
if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0)
goto error;
sprintf(buf, "AT#NCM=1,%u", gcd->active_context);
if (g_at_chat_send(gcd->chat, buf, none_prefix, NULL, NULL, NULL) == 0)
goto error;
sprintf(buf, "AT+CGACT=1,%u", gcd->active_context);
if (g_at_chat_send(gcd->chat, buf, none_prefix,
activate_cb, gc, NULL) > 0)
return;
error:
failed_setup(gc, NULL, FALSE);
}
static void telitncm_gprs_activate_primary(struct ofono_gprs_context *gc,
const struct ofono_gprs_primary_context *ctx,
ofono_gprs_context_cb_t cb, void *data)
{
struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
char buf[OFONO_GPRS_MAX_APN_LENGTH + 128];
int len = 0;
DBG("cid %u", ctx->cid);
gcd->active_context = ctx->cid;
gcd->cb = cb;
gcd->cb_data = data;
memcpy(gcd->username, ctx->username, sizeof(ctx->username));
memcpy(gcd->password, ctx->password, sizeof(ctx->password));
gcd->state = STATE_ENABLING;
gcd->proto = ctx->proto;
/* We only support CHAP and PAP */
switch (ctx->auth_method) {
case OFONO_GPRS_AUTH_METHOD_CHAP:
gcd->auth_method = AUTH_METHOD_CHAP;
break;
case OFONO_GPRS_AUTH_METHOD_PAP:
gcd->auth_method = AUTH_METHOD_PAP;
break;
default:
goto error;
}
switch (ctx->proto) {
case OFONO_GPRS_PROTO_IP:
len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IP\"",
ctx->cid);
break;
case OFONO_GPRS_PROTO_IPV6:
len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV6\"",
ctx->cid);
break;
case OFONO_GPRS_PROTO_IPV4V6:
len = snprintf(buf, sizeof(buf), "AT+CGDCONT=%u,\"IPV4V6\"",
ctx->cid);
break;
}
if (ctx->apn)
snprintf(buf + len, sizeof(buf) - len - 3,
",\"%s\"", ctx->apn);
if (g_at_chat_send(gcd->chat, buf, none_prefix,
setup_cb, gc, NULL) > 0)
return;
error:
CALLBACK_WITH_FAILURE(cb, data);
}
static void deactivate_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_gprs_context *gc = user_data;
struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
DBG("ok %d", ok);
gcd->active_context = 0;
gcd->state = STATE_IDLE;
CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
}
static void telitncm_gprs_deactivate_primary(struct ofono_gprs_context *gc,
unsigned int cid,
ofono_gprs_context_cb_t cb, void *data)
{
struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
char buf[64];
DBG("cid %u", cid);
gcd->state = STATE_DISABLING;
gcd->cb = cb;
gcd->cb_data = data;
sprintf(buf, "AT+CGACT=0,%u", gcd->active_context);
if (g_at_chat_send(gcd->chat, buf, none_prefix,
deactivate_cb, gc, NULL) > 0)
return;
CALLBACK_WITH_SUCCESS(cb, data);
}
static void cgev_notify(GAtResult *result, gpointer user_data)
{
struct ofono_gprs_context *gc = user_data;
struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
const char *event;
int cid;
GAtResultIter iter;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CGEV:"))
return;
if (!g_at_result_iter_next_unquoted_string(&iter, &event))
return;
if (g_str_has_prefix(event, "NW DEACT") == FALSE)
return;
if (!g_at_result_iter_skip_next(&iter))
return;
if (!g_at_result_iter_next_number(&iter, &cid))
return;
DBG("cid %d", cid);
if ((unsigned int) cid != gcd->active_context)
return;
ofono_gprs_context_deactivated(gc, gcd->active_context);
gcd->active_context = 0;
gcd->state = STATE_IDLE;
}
static int telitncm_gprs_context_probe(struct ofono_gprs_context *gc,
unsigned int vendor, void *data)
{
GAtChat *chat = data;
struct gprs_context_data *gcd;
DBG("");
gcd = g_try_new0(struct gprs_context_data, 1);
if (gcd == NULL)
return -ENOMEM;
gcd->chat = g_at_chat_clone(chat);
ofono_gprs_context_set_data(gc, gcd);
g_at_chat_register(chat, "+CGEV:", cgev_notify, FALSE, gc, NULL);
return 0;
}
static void telitncm_gprs_context_remove(struct ofono_gprs_context *gc)
{
struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
DBG("");
ofono_gprs_context_set_data(gc, NULL);
g_at_chat_unref(gcd->chat);
g_free(gcd);
}
static struct ofono_gprs_context_driver driver = {
.name = "telitncmmodem",
.probe = telitncm_gprs_context_probe,
.remove = telitncm_gprs_context_remove,
.activate_primary = telitncm_gprs_activate_primary,
.deactivate_primary = telitncm_gprs_deactivate_primary,
};
void telitncm_gprs_context_init(void)
{
ofono_gprs_context_driver_register(&driver);
}
void telitncm_gprs_context_exit(void)
{
ofono_gprs_context_driver_unregister(&driver);
}

View File

@@ -35,6 +35,7 @@
static int telitmodem_init(void)
{
telit_location_reporting_init();
telitncm_gprs_context_init();
return 0;
}
@@ -42,6 +43,7 @@ static int telitmodem_init(void)
static void telitmodem_exit(void)
{
telit_location_reporting_exit();
telitncm_gprs_context_exit();
}
OFONO_PLUGIN_DEFINE(telitmodem, "Telit modem driver", VERSION,

View File

@@ -23,3 +23,5 @@
extern void telit_location_reporting_init();
extern void telit_location_reporting_exit();
extern void telitncm_gprs_context_init();
extern void telitncm_gprs_context_exit();

View File

@@ -43,6 +43,7 @@
static const char *none_prefix[] = { NULL };
static const char *cgcontrdp_prefix[] = { "+CGCONTRDP:", NULL };
static const char *uipaddr_prefix[] = { "+UIPADDR:", NULL };
struct gprs_context_data {
GAtChat *chat;
@@ -51,6 +52,44 @@ struct gprs_context_data {
void *cb_data;
};
static void uipaddr_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_gprs_context *gc = user_data;
struct gprs_context_data *gcd = ofono_gprs_context_get_data(gc);
GAtResultIter iter;
const char *gw = NULL;
const char *netmask = NULL;
DBG("ok %d", ok);
if (!ok) {
CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
return;
}
g_at_result_iter_init(&iter, result);
while (g_at_result_iter_next(&iter, "+UIPADDR:")) {
g_at_result_iter_skip_next(&iter);
g_at_result_iter_skip_next(&iter);
if (!g_at_result_iter_next_string(&iter, &gw))
break;
if (!g_at_result_iter_next_string(&iter, &netmask))
break;
}
if (gw)
ofono_gprs_context_set_ipv4_gateway(gc, gw);
if (netmask)
ofono_gprs_context_set_ipv4_netmask(gc, netmask);
CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
}
/*
* CGCONTRDP returns addr + netmask in the same string in the form
* of "a.b.c.d.m.m.m.m" for IPv4. IPv6 is not supported so we ignore it.
@@ -113,6 +152,7 @@ static void cgcontrdp_cb(gboolean ok, GAtResult *result, gpointer user_data)
const char *laddrnetmask = NULL;
const char *gw = NULL;
const char *dns[3] = { NULL, NULL, NULL };
char buf[64];
DBG("ok %d", ok);
@@ -159,6 +199,17 @@ static void cgcontrdp_cb(gboolean ok, GAtResult *result, gpointer user_data)
if (dns[0])
ofono_gprs_context_set_ipv4_dns_servers(gc, dns);
/*
* Some older versions of Toby L2 need to issue AT+UIPADDR to get the
* the correct gateway and netmask. The newer version will return an
* empty ok reply.
*/
snprintf(buf, sizeof(buf), "AT+UIPADDR=%u", gcd->active_context);
if (g_at_chat_send(gcd->chat, buf, uipaddr_prefix,
uipaddr_cb, gc, NULL) > 0)
return;
/* Even if UIPADDR failed, we still have enough data. */
CALLBACK_WITH_SUCCESS(gcd->cb, gcd->cb_data);
}
@@ -443,6 +494,7 @@ static void ublox_gprs_context_remove(struct ofono_gprs_context *gc)
g_at_chat_unref(gcd->chat);
memset(gcd, 0, sizeof(*gcd));
g_free(gcd);
}
static struct ofono_gprs_context_driver driver = {

View File

@@ -0,0 +1,142 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2016 Endocode AG. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define _GNU_SOURCE
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <glib.h>
#include <ofono/modem.h>
#include <ofono/gprs-context.h>
#include <ofono/log.h>
#include <ofono/lte.h>
#include "gatchat.h"
#include "gatresult.h"
#include "ubloxmodem.h"
static const char *ucgdflt_prefix[] = { "+UCGDFLT:", NULL };
struct lte_driver_data {
GAtChat *chat;
};
static void ucgdflt_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_lte_cb_t cb = cbd->cb;
struct ofono_error error;
DBG("ok %d", ok);
decode_at_error(&error, g_at_result_final_response(result));
cb(&error, cbd->data);
}
static void ublox_lte_set_default_attach_info(const struct ofono_lte *lte,
const struct ofono_lte_default_attach_info *info,
ofono_lte_cb_t cb, void *data)
{
struct lte_driver_data *ldd = ofono_lte_get_data(lte);
char buf[32 + OFONO_GPRS_MAX_APN_LENGTH + 1];
struct cb_data *cbd = cb_data_new(cb, data);
DBG("LTE config with APN: %s", info->apn);
if (strlen(info->apn) > 0)
snprintf(buf, sizeof(buf), "AT+UCGDFLT=0,\"IP\",\"%s\"",
info->apn);
else
snprintf(buf, sizeof(buf), "AT+UCGDFLT=0");
/* We can't do much in case of failure so don't check response. */
if (g_at_chat_send(ldd->chat, buf, ucgdflt_prefix,
ucgdflt_cb, cbd, g_free) > 0)
return;
CALLBACK_WITH_FAILURE(cb, data);
}
static gboolean lte_delayed_register(gpointer user_data)
{
struct ofono_lte *lte = user_data;
ofono_lte_register(lte);
return FALSE;
}
static int ublox_lte_probe(struct ofono_lte *lte, void *data)
{
GAtChat *chat = data;
struct lte_driver_data *ldd;
DBG("ublox lte probe");
ldd = g_try_new0(struct lte_driver_data, 1);
if (!ldd)
return -ENOMEM;
ldd->chat = g_at_chat_clone(chat);
ofono_lte_set_data(lte, ldd);
g_idle_add(lte_delayed_register, lte);
return 0;
}
static void ublox_lte_remove(struct ofono_lte *lte)
{
struct lte_driver_data *ldd = ofono_lte_get_data(lte);
DBG("ublox lte remove");
g_at_chat_unref(ldd->chat);
ofono_lte_set_data(lte, NULL);
g_free(ldd);
}
static struct ofono_lte_driver driver = {
.name = UBLOXMODEM,
.probe = ublox_lte_probe,
.remove = ublox_lte_remove,
.set_default_attach_info = ublox_lte_set_default_attach_info,
};
void ublox_lte_init(void)
{
ofono_lte_driver_register(&driver);
}
void ublox_lte_exit(void)
{
ofono_lte_driver_unregister(&driver);
}

View File

@@ -0,0 +1,354 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2016 EndoCode AG. 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 <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <glib.h>
#include <ofono/log.h>
#include <ofono/modem.h>
#include <ofono/netreg.h>
#include <ofono/netmon.h>
#include "gatchat.h"
#include "gatresult.h"
#include "common.h"
#include "ubloxmodem.h"
#include "drivers/atmodem/vendor.h"
static const char *cops_prefix[] = { "+COPS:", NULL };
static const char *cesq_prefix[] = { "+CESQ:", NULL };
struct netmon_driver_data {
GAtChat *chat;
};
struct req_cb_data {
gint ref_count; /* Ref count */
struct ofono_netmon *netmon;
ofono_netmon_cb_t cb;
void *data;
struct ofono_network_operator op;
int rxlev; /* CESQ: Received Signal Strength Indication */
int ber; /* CESQ: Bit Error Rate */
int rscp; /* CESQ: Received Signal Code Powe */
int rsrp; /* CESQ: Reference Signal Received Power */
int ecn0; /* CESQ: Received Energy Ratio */
int rsrq; /* CESQ: Reference Signal Received Quality */
};
/*
* Returns the appropriate radio access technology.
*
* If we can not resolve to a specific radio access technolgy
* we return OFONO_NETMON_CELL_TYPE_GSM by default.
*/
static int ublox_map_radio_access_technology(int tech)
{
switch (tech) {
case ACCESS_TECHNOLOGY_GSM:
case ACCESS_TECHNOLOGY_GSM_COMPACT:
return OFONO_NETMON_CELL_TYPE_GSM;
case ACCESS_TECHNOLOGY_UTRAN:
case ACCESS_TECHNOLOGY_UTRAN_HSDPA:
case ACCESS_TECHNOLOGY_UTRAN_HSUPA:
case ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA:
return OFONO_NETMON_CELL_TYPE_UMTS;
case ACCESS_TECHNOLOGY_EUTRAN:
return OFONO_NETMON_CELL_TYPE_LTE;
}
return OFONO_NETMON_CELL_TYPE_GSM;
}
static inline struct req_cb_data *req_cb_data_new0(void *cb, void *data,
void *user)
{
struct req_cb_data *ret = g_new0(struct req_cb_data, 1);
ret->ref_count = 1;
ret->cb = cb;
ret->data = data;
ret->netmon = user;
ret->rxlev = -1;
ret->ber = -1;
ret->rscp = -1;
ret->rsrp = -1;
ret->ecn0 = -1;
ret->rsrq = -1;
return ret;
}
static inline struct req_cb_data *req_cb_data_ref(struct req_cb_data *cbd)
{
if (cbd == NULL)
return NULL;
g_atomic_int_inc(&cbd->ref_count);
return cbd;
}
static void req_cb_data_unref(gpointer user_data)
{
gboolean is_zero;
struct req_cb_data *cbd = user_data;
if (cbd == NULL)
return;
is_zero = g_atomic_int_dec_and_test(&cbd->ref_count);
if (is_zero == TRUE)
g_free(cbd);
}
static gboolean ublox_delayed_register(gpointer user_data)
{
struct ofono_netmon *netmon = user_data;
ofono_netmon_register(netmon);
return FALSE;
}
static void ublox_netmon_finish_success(struct req_cb_data *cbd)
{
struct ofono_netmon *nm = cbd->netmon;
ofono_netmon_serving_cell_notify(nm,
cbd->op.tech,
OFONO_NETMON_INFO_RXLEV, cbd->rxlev,
OFONO_NETMON_INFO_BER, cbd->ber,
OFONO_NETMON_INFO_RSCP, cbd->rscp,
OFONO_NETMON_INFO_ECN0, cbd->ecn0,
OFONO_NETMON_INFO_RSRQ, cbd->rsrq,
OFONO_NETMON_INFO_RSRP, cbd->rsrp,
OFONO_NETMON_INFO_INVALID);
CALLBACK_WITH_SUCCESS(cbd->cb, cbd->data);
}
static void cesq_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
enum cesq_ofono_netmon_info {
CESQ_RXLEV,
CESQ_BER,
CESQ_RSCP,
CESQ_ECN0,
CESQ_RSRQ,
CESQ_RSRP,
_MAX,
};
struct req_cb_data *cbd = user_data;
struct ofono_error error;
GAtResultIter iter;
int idx, number;
DBG("ok %d", ok);
decode_at_error(&error, g_at_result_final_response(result));
if (!ok) {
CALLBACK_WITH_FAILURE(cbd->cb, cbd->data);
return;
}
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "+CESQ:")) {
DBG(" CESQ: no result ");
goto out;
}
for (idx = 0; idx < _MAX; idx++) {
ok = g_at_result_iter_next_number(&iter, &number);
if (!ok) {
/* Ignore and do not fail */
DBG(" CESQ: error parsing idx: %d ", idx);
goto out;
}
switch (idx) {
case CESQ_RXLEV:
cbd->rxlev = number != 99 ? number:cbd->rxlev;
break;
case CESQ_BER:
cbd->ber = number != 99 ? number:cbd->ber;
break;
case CESQ_RSCP:
cbd->rscp = number != 255 ? number:cbd->rscp;
break;
case CESQ_ECN0:
cbd->ecn0 = number != 255 ? number:cbd->ecn0;
break;
case CESQ_RSRQ:
cbd->rsrq = number != 255 ? number:cbd->rsrq;
break;
case CESQ_RSRP:
cbd->rsrp = number != 255 ? number:cbd->rsrp;
break;
}
}
DBG(" RXLEV %d ", cbd->rxlev);
DBG(" BER %d ", cbd->ber);
DBG(" RSCP %d ", cbd->rscp);
DBG(" ECN0 %d ", cbd->ecn0);
DBG(" RSRQ %d ", cbd->rsrq);
DBG(" RSRP %d ", cbd->rsrp);
/*
* We never fail at this point we always send what we collected so
* far
*/
out:
ublox_netmon_finish_success(cbd);
}
static void cops_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct req_cb_data *cbd = user_data;
struct ofono_netmon *nm = cbd->netmon;
struct netmon_driver_data *nmd = ofono_netmon_get_data(nm);
struct ofono_error error;
GAtResultIter iter;
int tech;
DBG("ok %d", ok);
decode_at_error(&error, g_at_result_final_response(result));
if (!ok) {
CALLBACK_WITH_FAILURE(cbd->cb, cbd->data);
return;
}
g_at_result_iter_init(&iter, result);
/* Do not fail */
if (!g_at_result_iter_next(&iter, "+COPS:")) {
CALLBACK_WITH_SUCCESS(cbd->cb, cbd->data);
return;
}
g_at_result_iter_skip_next(&iter);
g_at_result_iter_skip_next(&iter);
g_at_result_iter_skip_next(&iter);
/* Default to GSM */
if (g_at_result_iter_next_number(&iter, &tech) == FALSE)
cbd->op.tech = OFONO_NETMON_CELL_TYPE_GSM;
else
cbd->op.tech = ublox_map_radio_access_technology(tech);
cbd = req_cb_data_ref(cbd);
if (g_at_chat_send(nmd->chat, "AT+CESQ", cesq_prefix,
cesq_cb, cbd, req_cb_data_unref) == 0) {
CALLBACK_WITH_FAILURE(cbd->cb, cbd->data);
req_cb_data_unref(cbd);
}
}
static void ublox_netmon_request_update(struct ofono_netmon *netmon,
ofono_netmon_cb_t cb, void *data)
{
struct netmon_driver_data *nmd = ofono_netmon_get_data(netmon);
struct req_cb_data *cbd;
DBG("ublox netmon request update");
cbd = req_cb_data_new0(cb, data, netmon);
if (g_at_chat_send(nmd->chat, "AT+COPS?", cops_prefix,
cops_cb, cbd, req_cb_data_unref) == 0) {
CALLBACK_WITH_FAILURE(cbd->cb, cbd->data);
req_cb_data_unref(cbd);
}
}
static int ublox_netmon_probe(struct ofono_netmon *netmon,
unsigned int vendor, void *user)
{
GAtChat *chat = user;
struct netmon_driver_data *nmd;
DBG("ublox netmon probe");
nmd = g_try_new0(struct netmon_driver_data, 1);
if (nmd == NULL)
return -ENOMEM;
nmd->chat = g_at_chat_clone(chat);
ofono_netmon_set_data(netmon, nmd);
g_idle_add(ublox_delayed_register, netmon);
return 0;
}
static void ublox_netmon_remove(struct ofono_netmon *netmon)
{
struct netmon_driver_data *nmd = ofono_netmon_get_data(netmon);
DBG("ublox netmon remove");
g_at_chat_unref(nmd->chat);
ofono_netmon_set_data(netmon, NULL);
g_free(nmd);
}
static struct ofono_netmon_driver driver = {
.name = UBLOXMODEM,
.probe = ublox_netmon_probe,
.remove = ublox_netmon_remove,
.request_update = ublox_netmon_request_update,
};
void ublox_netmon_init(void)
{
ofono_netmon_driver_register(&driver);
}
void ublox_netmon_exit(void)
{
ofono_netmon_driver_unregister(&driver);
}

View File

@@ -29,12 +29,15 @@
#define OFONO_API_SUBJECT_TO_CHANGE
#include <ofono/plugin.h>
#include <ofono/types.h>
#include <ofono/modem.h>
#include "ubloxmodem.h"
static int ubloxmodem_init(void)
{
ublox_gprs_context_init();
ublox_netmon_init();
ublox_lte_init();
return 0;
}
@@ -42,6 +45,8 @@ static int ubloxmodem_init(void)
static void ubloxmodem_exit(void)
{
ublox_gprs_context_exit();
ublox_netmon_exit();
ublox_lte_exit();
}
OFONO_PLUGIN_DEFINE(ubloxmodem, "U-Blox Toby L2 high speed modem driver",

View File

@@ -21,5 +21,13 @@
#include <drivers/atmodem/atutil.h>
#define UBLOXMODEM "ubloxmodem"
extern void ublox_gprs_context_init(void);
extern void ublox_gprs_context_exit(void);
extern void ublox_netmon_init(void);
extern void ublox_netmon_exit(void);
extern void ublox_lte_init(void);
extern void ublox_lte_exit(void);

View File

@@ -116,66 +116,109 @@ static inline void debug(GAtMux *mux, const char *format, ...)
static void dispatch_sources(GAtMuxChannel *channel, GIOCondition condition)
{
GAtMuxWatch *source;
GSList *c;
GSList *p;
GSList *t;
GSList *refs;
/*
* Don't reference destroyed sources, they may have zero reference
* count if this function is invoked from the source's finalize
* callback, in which case incrementing and then decrementing
* the count would result in double free (first when we decrement
* the reference count and then when we return from the finalize
* callback).
*/
p = NULL;
refs = NULL;
for (c = channel->sources; c; c = c->next) {
GSource *s = c->data;
if (!g_source_is_destroyed(s)) {
GSList *l = g_slist_append(NULL, g_source_ref(s));
if (p)
p->next = l;
else
refs = l;
p = l;
}
}
/*
* Keep the references to all sources for the duration of the loop.
* Callbacks may add and remove the sources, i.e. channel->sources
* may keep changing during the loop.
*/
for (c = refs; c; c = c->next) {
GAtMuxWatch *w = c->data;
GSource *s = &w->source;
if (g_source_is_destroyed(s))
continue;
debug(channel->mux, "checking source: %p", s);
if (condition & w->condition) {
gpointer user_data = NULL;
GSourceFunc callback = NULL;
GSourceCallbackFuncs *cb_funcs = s->callback_funcs;
gpointer cb_data = s->callback_data;
gboolean destroy;
debug(channel->mux, "dispatching source: %p", s);
if (cb_funcs) {
cb_funcs->ref(cb_data);
cb_funcs->get(cb_data, s, &callback,
&user_data);
}
destroy = !s->source_funcs->dispatch(s, callback,
user_data);
if (cb_funcs)
cb_funcs->unref(cb_data);
if (destroy) {
debug(channel->mux, "removing source: %p", s);
g_source_destroy(s);
}
}
}
/*
* Remove destroyed sources from channel->sources. During this
* loop we are not invoking any callbacks, so the consistency is
* guaranteed.
*/
p = NULL;
c = channel->sources;
while (c) {
gboolean destroy = FALSE;
source = c->data;
debug(channel->mux, "checking source: %p", source);
if (condition & source->condition) {
gpointer user_data = NULL;
GSourceFunc callback = NULL;
GSourceCallbackFuncs *cb_funcs;
gpointer cb_data;
gboolean (*dispatch) (GSource *, GSourceFunc, gpointer);
debug(channel->mux, "dispatching source: %p", source);
dispatch = source->source.source_funcs->dispatch;
cb_funcs = source->source.callback_funcs;
cb_data = source->source.callback_data;
if (cb_funcs)
cb_funcs->ref(cb_data);
if (cb_funcs)
cb_funcs->get(cb_data, (GSource *) source,
&callback, &user_data);
destroy = !dispatch((GSource *) source, callback,
user_data);
if (cb_funcs)
cb_funcs->unref(cb_data);
}
if (destroy) {
debug(channel->mux, "removing source: %p", source);
g_source_destroy((GSource *) source);
GSList *n = c->next;
GSource *s = c->data;
if (g_source_is_destroyed(s)) {
if (p)
p->next = c->next;
p->next = n;
else
channel->sources = c->next;
channel->sources = n;
t = c;
c = c->next;
g_slist_free_1(t);
g_slist_free_1(c);
} else {
p = c;
c = c->next;
}
c = n;
}
/* Release temporary references */
g_slist_free_full(refs, (GDestroyNotify) g_source_unref);
}
static gboolean received_data(GIOChannel *channel, GIOCondition cond,
@@ -422,7 +465,9 @@ static gboolean watch_dispatch(GSource *source, GSourceFunc callback,
static void watch_finalize(GSource *source)
{
GAtMuxWatch *watch = (GAtMuxWatch *) source;
GAtMuxChannel *dlc = (GAtMuxChannel *) watch->channel;
dlc->sources = g_slist_remove(dlc->sources, watch);
g_io_channel_unref(watch->channel);
}
@@ -639,6 +684,9 @@ gboolean g_at_mux_shutdown(GAtMux *mux)
if (mux->read_watch > 0)
g_source_remove(mux->read_watch);
if (mux->write_watch > 0)
g_source_remove(mux->write_watch);
for (i = 0; i < MAX_CHANNELS; i++) {
if (mux->dlcs[i] == NULL)
continue;

View File

@@ -40,7 +40,6 @@
#include "crc-ccitt.h"
#include "ppp.h"
#define DEFAULT_MRU 1500
#define DEFAULT_MTU 1500
#define PPP_ADDR_FIELD 0xff
@@ -66,7 +65,6 @@ struct _GAtPPP {
struct ppp_chap *chap;
struct ppp_pap *pap;
GAtHDLC *hdlc;
gint mru;
gint mtu;
char username[256];
char password[256];
@@ -830,7 +828,6 @@ static GAtPPP *ppp_init_common(gboolean is_server, guint32 ip)
ppp->fd = -1;
/* set options to defaults */
ppp->mru = DEFAULT_MRU;
ppp->mtu = DEFAULT_MTU;
/* initialize the lcp state */

View File

@@ -309,6 +309,12 @@ static GAtSyntaxResult gsm_permissive_feed(GAtSyntax *syntax,
case GSM_PERMISSIVE_STATE_RESPONSE_STRING:
if (byte == '"')
syntax->state = GSM_PERMISSIVE_STATE_RESPONSE;
else if (byte == '\r') {
syntax->state = GSM_PERMISSIVE_STATE_IDLE;
i += 1;
res = G_AT_SYNTAX_RESULT_LINE;
goto out;
}
break;
case GSM_PERMISSIVE_STATE_GUESS_PDU:

View File

@@ -63,6 +63,7 @@ extern "C" {
#define OFONO_NETWORK_TIME_INTERFACE OFONO_SERVICE ".NetworkTime"
#define OFONO_SIRI_INTERFACE OFONO_SERVICE ".Siri"
#define OFONO_NETMON_INTERFACE OFONO_SERVICE ".NetworkMonitor"
#define OFONO_LTE_INTERFACE OFONO_SERVICE ".LongTermEvolution"
/* CDMA Interfaces */
#define OFONO_CDMA_VOICECALL_MANAGER_INTERFACE "org.ofono.cdma.VoiceCallManager"

View File

@@ -80,7 +80,6 @@ void ofono_gprs_set_cid_range(struct ofono_gprs *gprs,
void ofono_gprs_add_context(struct ofono_gprs *gprs,
struct ofono_gprs_context *gc);
ofono_bool_t ofono_gprs_get_roaming_allowed(struct ofono_gprs *gprs);
void ofono_gprs_cid_activated(struct ofono_gprs *gprs, unsigned int cid,
const char *apn);

67
ofono/include/lte.h Normal file
View File

@@ -0,0 +1,67 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2016 Endocode AG. 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
*
*/
#ifndef __OFONO_LTE_H
#define __OFONO_LTE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <ofono/types.h>
struct ofono_lte;
struct ofono_lte_default_attach_info {
char apn[OFONO_GPRS_MAX_APN_LENGTH + 1];
};
typedef void (*ofono_lte_cb_t)(const struct ofono_error *error, void *data);
struct ofono_lte_driver {
const char *name;
int (*probe)(struct ofono_lte *lte, void *data);
void (*remove)(struct ofono_lte *lte);
void (*set_default_attach_info)(const struct ofono_lte *lte,
const struct ofono_lte_default_attach_info *info,
ofono_lte_cb_t cb, void *data);
};
int ofono_lte_driver_register(const struct ofono_lte_driver *d);
void ofono_lte_driver_unregister(const struct ofono_lte_driver *d);
struct ofono_lte *ofono_lte_create(struct ofono_modem *modem,
const char *driver, void *data);
void ofono_lte_register(struct ofono_lte *lte);
void ofono_lte_remove(struct ofono_lte *lte);
void ofono_lte_set_data(struct ofono_lte *lte, void *data);
void *ofono_lte_get_data(const struct ofono_lte *lte);
#ifdef __cplusplus
}
#endif
#endif

View File

@@ -29,6 +29,7 @@ extern "C" {
#include <ofono/types.h>
struct ofono_modem;
struct ofono_sim;
enum ofono_modem_type {
OFONO_MODEM_TYPE_HARDWARE = 0,
@@ -80,6 +81,7 @@ void ofono_modem_remove_interface(struct ofono_modem *modem,
const char *interface);
const char *ofono_modem_get_path(struct ofono_modem *modem);
struct ofono_sim *ofono_modem_get_sim(struct ofono_modem *modem);
void ofono_modem_set_data(struct ofono_modem *modem, void *data);
void *ofono_modem_get_data(struct ofono_modem *modem);

View File

@@ -59,6 +59,13 @@ enum ofono_netmon_info {
OFONO_NETMON_INFO_RSSI, /* int */
OFONO_NETMON_INFO_TIMING_ADVANCE, /* int */
OFONO_NETMON_INFO_PSC, /* int */
OFONO_NETMON_INFO_RSCP, /* int */
OFONO_NETMON_INFO_ECN0, /* int */
OFONO_NETMON_INFO_RSRQ, /* int */
OFONO_NETMON_INFO_RSRP, /* int */
OFONO_NETMON_INFO_EARFCN, /* int */
OFONO_NETMON_INFO_EBAND, /* int */
OFONO_NETMON_INFO_CQI, /* int */
OFONO_NETMON_INFO_INVALID,
};

View File

@@ -0,0 +1,114 @@
/*
* 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_CELL_INFO_H
#define SAILFISH_CELL_INFO_H
#include <glib.h>
enum sailfish_cell_type {
SAILFISH_CELL_TYPE_GSM,
SAILFISH_CELL_TYPE_WCDMA,
SAILFISH_CELL_TYPE_LTE
};
struct sailfish_cell_info_gsm {
int mcc; /* Mobile Country Code (0..999) */
int mnc; /* Mobile Network Code (0..999) */
int lac; /* Location Area Code (0..65535) */
int cid; /* GSM Cell Identity (0..65535) TS 27.007 */
int arfcn; /* 16-bit GSM Absolute RF channel number */
int bsic; /* 6-bit Base Station Identity Code */
int signalStrength; /* (0-31, 99) TS 27.007 */
int bitErrorRate; /* (0-7, 99) TS 27.007 */
int timingAdvance; /* Timing Advance. 1 period = 48/13 us */
};
struct sailfish_cell_info_wcdma {
int mcc; /* Mobile Country Code (0..999) */
int mnc; /* Mobile Network Code (0..999) */
int lac; /* Location Area Code (0..65535) */
int cid; /* UMTS Cell Identity (0..268435455) TS 25.331 */
int psc; /* Primary Scrambling Code (0..511) TS 25.331) */
int uarfcn; /* 16-bit UMTS Absolute RF Channel Number */
int signalStrength; /* (0-31, 99) TS 27.007 */
int bitErrorRate; /* (0-7, 99) TS 27.007 */
};
struct sailfish_cell_info_lte {
int mcc; /* Mobile Country Code (0..999) */
int mnc; /* Mobile Network Code (0..999) */
int ci; /* Cell Identity */
int pci; /* Physical cell id (0..503) */
int tac; /* Tracking area code */
int earfcn; /* 18-bit LTE Absolute RC Channel Number */
int signalStrength; /* (0-31, 99) TS 27.007 8.5 */
int rsrp; /* Reference Signal Receive Power TS 36.133 */
int rsrq; /* Reference Signal Receive Quality TS 36.133 */
int rssnr; /* Reference Signal-to-Noise Ratio TS 36.101*/
int cqi; /* Channel Quality Indicator TS 36.101 */
int timingAdvance; /* (Distance = 300m/us) TS 36.321 */
};
struct sailfish_cell {
enum sailfish_cell_type type;
gboolean registered;
union {
struct sailfish_cell_info_gsm gsm;
struct sailfish_cell_info_wcdma wcdma;
struct sailfish_cell_info_lte lte;
} info;
};
struct sailfish_cell_info {
const struct sailfish_cell_info_proc *proc;
GSList *cells;
};
typedef void (*sailfish_cell_info_cb_t)(struct sailfish_cell_info *info,
void *arg);
struct sailfish_cell_info_proc {
void (*ref)(struct sailfish_cell_info *info);
void (*unref)(struct sailfish_cell_info *info);
gulong (*add_cells_changed_handler)(struct sailfish_cell_info *info,
sailfish_cell_info_cb_t cb, void *arg);
void (*remove_handler)(struct sailfish_cell_info *info, gulong id);
};
/* Utilities */
gint sailfish_cell_compare_func(gconstpointer v1, gconstpointer v2);
gint sailfish_cell_compare_location(const struct sailfish_cell *c1,
const struct sailfish_cell *c2);
/* Cell info object API */
struct sailfish_cell_info *sailfish_cell_info_ref
(struct sailfish_cell_info *info);
void sailfish_cell_info_unref(struct sailfish_cell_info *info);
gulong sailfish_cell_info_add_cells_changed_handler
(struct sailfish_cell_info *info,
sailfish_cell_info_cb_t cb, void *arg);
void sailfish_cell_info_remove_handler(struct sailfish_cell_info *info,
gulong id);
#endif /* SAILFISH_CELINFO_H */
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -13,8 +13,8 @@
* GNU General Public License for more details.
*/
#ifndef SAILFISHOS_MANAGER_H
#define SAILFISHOS_MANAGER_H
#ifndef SAILFISH_MANAGER_H
#define SAILFISH_MANAGER_H
struct ofono_modem;
@@ -30,6 +30,8 @@ struct sailfish_slot_driver;
struct sailfish_slot_driver_reg;
struct sailfish_slot_manager;
struct sailfish_slot_manager_impl;
struct sailfish_cell_info;
typedef void (*sailfish_slot_manager_impl_cb_t)
(struct sailfish_slot_manager_impl *impl, void *user_data);
@@ -81,14 +83,16 @@ struct sailfish_slot *sailfish_manager_slot_add
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);
const char *imeisv);
void sailfish_manager_set_sim_state(struct sailfish_slot *s,
enum sailfish_sim_state state);
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);
const char *message);
void sailfish_manager_error(struct sailfish_slot_manager *m, const char *key,
const char *message);
const char *message);
void sailfish_manager_set_cell_info(struct sailfish_slot *s,
struct sailfish_cell_info *ci);
/* Callbacks provided by slot plugins */
struct sailfish_slot_driver {
@@ -110,7 +114,7 @@ struct sailfish_slot_driver {
void (*slot_free)(struct sailfish_slot_impl *s);
};
#endif /* SAILFISHOS_MANAGER_H */
#endif /* SAILFISH_MANAGER_H */
/*
* Local Variables:

184
ofono/include/sms-filter.h Normal file
View File

@@ -0,0 +1,184 @@
/*
* 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 __OFONO_SMS_FILTER_H
#define __OFONO_SMS_FILTER_H
#ifdef __cplusplus
extern "C" {
#endif
#include <ofono/types.h>
struct ofono_modem;
/* 23.040 Section 9.1.2.5 */
enum ofono_sms_number_type {
OFONO_SMS_NUMBER_TYPE_UNKNOWN = 0,
OFONO_SMS_NUMBER_TYPE_INTERNATIONAL = 1,
OFONO_SMS_NUMBER_TYPE_NATIONAL = 2,
OFONO_SMS_NUMBER_TYPE_NETWORK_SPECIFIC = 3,
OFONO_SMS_NUMBER_TYPE_SUBSCRIBER = 4,
OFONO_SMS_NUMBER_TYPE_ALPHANUMERIC = 5,
OFONO_SMS_NUMBER_TYPE_ABBREVIATED = 6,
OFONO_SMS_NUMBER_TYPE_RESERVED = 7
};
/* 23.040 Section 9.1.2.5 */
enum ofono_sms_numbering_plan {
OFONO_SMS_NUMBERING_PLAN_UNKNOWN = 0,
OFONO_SMS_NUMBERING_PLAN_ISDN = 1,
OFONO_SMS_NUMBERING_PLAN_DATA = 3,
OFONO_SMS_NUMBERING_PLAN_TELEX = 4,
OFONO_SMS_NUMBERING_PLAN_SC1 = 5,
OFONO_SMS_NUMBERING_PLAN_SC2 = 6,
OFONO_SMS_NUMBERING_PLAN_NATIONAL = 8,
OFONO_SMS_NUMBERING_PLAN_PRIVATE = 9,
OFONO_SMS_NUMBERING_PLAN_ERMES = 10,
OFONO_SMS_NUMBERING_PLAN_RESERVED = 15
};
enum ofono_sms_class {
OFONO_SMS_CLASS_0 = 0,
OFONO_SMS_CLASS_1 = 1,
OFONO_SMS_CLASS_2 = 2,
OFONO_SMS_CLASS_3 = 3,
OFONO_SMS_CLASS_UNSPECIFIED = 4,
};
struct ofono_sms_address {
enum ofono_sms_number_type number_type;
enum ofono_sms_numbering_plan numbering_plan;
/*
* An alphanum TP-OA is 10 7-bit coded octets, which can carry
* 11 8-bit characters. 22 bytes + terminator in UTF-8.
*/
char address[23];
};
struct ofono_sms_scts {
unsigned char year;
unsigned char month;
unsigned char day;
unsigned char hour;
unsigned char minute;
unsigned char second;
ofono_bool_t has_timezone;
unsigned char timezone;
};
enum ofono_sms_filter_result {
OFONO_SMS_FILTER_DROP, /* Stop processing and drop the message */
OFONO_SMS_FILTER_CONTINUE /* Run the next filter */
};
typedef void (*ofono_sms_filter_send_text_cb_t)
(enum ofono_sms_filter_result result,
const struct ofono_sms_address *addr,
const char *message,
void *data);
typedef void (*ofono_sms_filter_send_datagram_cb_t)
(enum ofono_sms_filter_result result,
const struct ofono_sms_address *addr,
int dst_port, int src_port,
const unsigned char *buf, unsigned int len,
void *data);
typedef void (*ofono_sms_filter_recv_text_cb_t)
(enum ofono_sms_filter_result result,
const struct ofono_uuid *uuid,
const char *message,
enum ofono_sms_class cls,
const struct ofono_sms_address *addr,
const struct ofono_sms_scts *scts,
void *data);
typedef void (*ofono_sms_filter_recv_datagram_cb_t)
(enum ofono_sms_filter_result result,
const struct ofono_uuid *uuid,
int dst_port, int src_port,
const unsigned char *buf, unsigned int len,
const struct ofono_sms_address *addr,
const struct ofono_sms_scts *scts,
void *data);
#define OFONO_SMS_FILTER_PRIORITY_LOW (-100)
#define OFONO_SMS_FILTER_PRIORITY_DEFAULT (0)
#define OFONO_SMS_FILTER_PRIORITY_HIGH (100)
/*
* The filter callbacks either invoke the completion callback directly
* or return the id of the cancellable asynchronous operation (but never
* both). If non-zero value is returned, the completion callback has to
* be invoked later on a fresh stack. Once the asynchronous filtering
* operation is cancelled, the associated completion callback must not
* be invoked.
*
* The pointers passed to the filter callbacks are guaranteed to be
* valid until the filter calls the completion callback. The completion
* callback is never NULL.
*
* Please avoid making blocking D-Bus calls from the filter callbacks.
*/
struct ofono_sms_filter {
const char *name;
int priority;
unsigned int (*filter_send_text)(struct ofono_modem *modem,
const struct ofono_sms_address *addr,
const char *message,
ofono_sms_filter_send_text_cb_t cb,
void *data);
unsigned int (*filter_send_datagram)(struct ofono_modem *modem,
const struct ofono_sms_address *addr,
int dst_port, int src_port,
const unsigned char *buf, unsigned int len,
ofono_sms_filter_send_datagram_cb_t cb,
void *data);
unsigned int (*filter_recv_text)(struct ofono_modem *modem,
const struct ofono_uuid *uuid,
const char *message,
enum ofono_sms_class cls,
const struct ofono_sms_address *addr,
const struct ofono_sms_scts *scts,
ofono_sms_filter_recv_text_cb_t cb,
void *data);
unsigned int (*filter_recv_datagram)(struct ofono_modem *modem,
const struct ofono_uuid *uuid,
int dst_port, int src_port,
const unsigned char *buf, unsigned int len,
const struct ofono_sms_address *addr,
const struct ofono_sms_scts *scts,
ofono_sms_filter_recv_datagram_cb_t cb,
void *data);
void (*cancel)(unsigned int id);
};
int ofono_sms_filter_register(const struct ofono_sms_filter *filter);
void ofono_sms_filter_unregister(const struct ofono_sms_filter *filter);
#ifdef __cplusplus
}
#endif
#endif /* __OFONO_SMS_FILTER_H */
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -155,8 +155,10 @@ void bt_unregister_profile(DBusConnection *conn, const char *object)
return;
}
dbus_pending_call_set_notify(c, unregister_profile_cb, NULL, NULL);
dbus_pending_call_unref(c);
if (c) {
dbus_pending_call_set_notify(c, unregister_profile_cb, NULL, NULL);
dbus_pending_call_unref(c);
}
dbus_message_unref(msg);
}

View File

@@ -0,0 +1,175 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 Kerlink SA.
*
* 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 <errno.h>
#include <stdio.h>
#include <string.h>
#include <glib.h>
#define OFONO_API_SUBJECT_TO_CHANGE
#include <ofono/modem.h>
#include <ofono/gprs-provision.h>
#include <ofono/log.h>
#include <ofono/plugin.h>
/* STORAGEDIR may need to be redefined in unit tests */
#ifndef STORAGEDIR
# define STORAGEDIR DEFAULT_STORAGEDIR
#endif
#define CONFIG_FILE STORAGEDIR "/provisioning"
static int config_file_provision_get_settings(const char *mcc,
const char *mnc, const char *spn,
struct ofono_gprs_provision_data **settings,
int *count)
{
int result = 0;
GKeyFile *key_file = NULL;
char *setting_group = NULL;
char *value;
DBG("Finding settings for MCC %s, MNC %s, SPN '%s'", mcc, mnc, spn);
*count = 0;
*settings = NULL;
key_file = g_key_file_new();
if (!g_key_file_load_from_file(key_file, CONFIG_FILE, 0, NULL)) {
result = -ENOENT;
goto error;
}
setting_group = g_try_malloc(strlen("operator:") + strlen(mcc) +
strlen(mnc) + 2);
if (setting_group == NULL) {
result = -ENOMEM;
goto error;
}
sprintf(setting_group, "operator:%s,%s", mcc, mnc);
value = g_key_file_get_string(key_file, setting_group,
"internet.AccessPointName", NULL);
if (value == NULL)
goto error;
*settings = g_try_new0(struct ofono_gprs_provision_data, 1);
if (*settings == NULL) {
result = -ENOMEM;
goto error;
}
*count = 1;
(*settings)[0].type = OFONO_GPRS_CONTEXT_TYPE_INTERNET;
(*settings)[0].apn = value;
value = g_key_file_get_string(key_file, setting_group,
"internet.Username", NULL);
if (value != NULL)
(*settings)[0].username = value;
value = g_key_file_get_string(key_file, setting_group,
"internet.Password", NULL);
if (value != NULL)
(*settings)[0].password = value;
(*settings)[0].auth_method = OFONO_GPRS_AUTH_METHOD_CHAP;
value = g_key_file_get_string(key_file, setting_group,
"internet.AuthenticationMethod", NULL);
if (value != NULL) {
if (g_strcmp0(value, "chap") == 0)
(*settings)[0].auth_method =
OFONO_GPRS_AUTH_METHOD_CHAP;
else if (g_strcmp0(value, "pap") == 0)
(*settings)[0].auth_method =
OFONO_GPRS_AUTH_METHOD_PAP;
else
DBG("Unknown auth method: %s", value);
g_free(value);
}
(*settings)[0].proto = OFONO_GPRS_PROTO_IP;
value = g_key_file_get_string(key_file, setting_group,
"internet.Protocol", NULL);
if (value != NULL) {
DBG("CRO value:%s", value);
if (g_strcmp0(value, "ip") == 0) {
DBG("CRO value=ip");
(*settings)[0].proto = OFONO_GPRS_PROTO_IP;
} else if (g_strcmp0(value, "ipv6") == 0) {
DBG("CRO value=ipv6");
(*settings)[0].proto = OFONO_GPRS_PROTO_IPV6;
} else if (g_strcmp0(value, "dual") == 0)
(*settings)[0].proto = OFONO_GPRS_PROTO_IPV4V6;
else
DBG("Unknown protocol: %s", value);
g_free(value);
}
error:
if (key_file != NULL)
g_key_file_free(key_file);
if (setting_group != NULL)
g_free(setting_group);
if (result == 0 && *count > 0)
DBG("Found. APN:%s, proto:%d, auth_method:%d",
(*settings)[0].apn, (*settings)[0].proto,
(*settings)[0].auth_method);
else
DBG("Not found. Result:%d", result);
return result;
}
static struct ofono_gprs_provision_driver config_file_provision_driver = {
.name = "GPRS context provisioning",
.get_settings = config_file_provision_get_settings,
};
static int config_file_provision_init(void)
{
return ofono_gprs_provision_driver_register(
&config_file_provision_driver);
}
static void config_file_provision_exit(void)
{
ofono_gprs_provision_driver_unregister(
&config_file_provision_driver);
}
OFONO_PLUGIN_DEFINE(file_provision, "Gprs Provisioning Plugin",
VERSION, OFONO_PLUGIN_PRIORITY_HIGH,
config_file_provision_init,
config_file_provision_exit)

313
ofono/plugins/gemalto.c Normal file
View File

@@ -0,0 +1,313 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 Vincent Cesson. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <errno.h>
#include <stdlib.h>
#include <glib.h>
#include <gatchat.h>
#include <gattty.h>
#define OFONO_API_SUBJECT_TO_CHANGE
#include <ofono/plugin.h>
#include <ofono/log.h>
#include <ofono/modem.h>
#include <ofono/devinfo.h>
#include <ofono/netreg.h>
#include <ofono/phonebook.h>
#include <ofono/sim.h>
#include <ofono/sms.h>
#include <ofono/gprs.h>
#include <ofono/gprs-context.h>
#include <ofono/location-reporting.h>
#include <drivers/atmodem/atutil.h>
#include <drivers/atmodem/vendor.h>
static const char *none_prefix[] = { NULL };
struct gemalto_data {
GAtChat *app;
GAtChat *mdm;
struct ofono_sim *sim;
gboolean have_sim;
struct at_util_sim_state_query *sim_state_query;
};
static int gemalto_probe(struct ofono_modem *modem)
{
struct gemalto_data *data;
data = g_try_new0(struct gemalto_data, 1);
if (data == NULL)
return -ENOMEM;
ofono_modem_set_data(modem, data);
return 0;
}
static void gemalto_remove(struct ofono_modem *modem)
{
struct gemalto_data *data = ofono_modem_get_data(modem);
/* Cleanup potential SIM state polling */
at_util_sim_state_query_free(data->sim_state_query);
ofono_modem_set_data(modem, NULL);
g_free(data);
}
static void gemalto_debug(const char *str, void *user_data)
{
const char *prefix = user_data;
ofono_info("%s%s", prefix, str);
}
static GAtChat *open_device(const char *device)
{
GAtSyntax *syntax;
GIOChannel *channel;
GAtChat *chat;
DBG("Opening device %s", device);
channel = g_at_tty_open(device, NULL);
if (channel == NULL)
return NULL;
syntax = g_at_syntax_new_gsm_permissive();
chat = g_at_chat_new(channel, syntax);
g_at_syntax_unref(syntax);
g_io_channel_unref(channel);
if (chat == NULL)
return NULL;
return chat;
}
static void sim_state_cb(gboolean present, gpointer user_data)
{
struct ofono_modem *modem = user_data;
struct gemalto_data *data = ofono_modem_get_data(modem);
at_util_sim_state_query_free(data->sim_state_query);
data->sim_state_query = NULL;
data->have_sim = present;
ofono_modem_set_powered(modem, TRUE);
}
static void cfun_enable(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_modem *modem = user_data;
struct gemalto_data *data = ofono_modem_get_data(modem);
if (!ok) {
g_at_chat_unref(data->app);
data->app = NULL;
g_at_chat_unref(data->mdm);
data->mdm = NULL;
ofono_modem_set_powered(modem, FALSE);
return;
}
data->sim_state_query = at_util_sim_state_query_new(data->app,
2, 20, sim_state_cb, modem,
NULL);
}
static int gemalto_enable(struct ofono_modem *modem)
{
struct gemalto_data *data = ofono_modem_get_data(modem);
const char *app, *mdm;
DBG("%p", modem);
app = ofono_modem_get_string(modem, "Application");
mdm = ofono_modem_get_string(modem, "Modem");
if (app == NULL || mdm == NULL)
return -EINVAL;
/* Open devices */
data->app = open_device(app);
if (data->app == NULL)
return -EINVAL;
data->mdm = open_device(mdm);
if (data->mdm == NULL) {
g_at_chat_unref(data->app);
data->app = NULL;
return -EINVAL;
}
if (getenv("OFONO_AT_DEBUG")) {
g_at_chat_set_debug(data->app, gemalto_debug, "App");
g_at_chat_set_debug(data->mdm, gemalto_debug, "Mdm");
}
g_at_chat_send(data->mdm, "ATE0", none_prefix, NULL, NULL, NULL);
g_at_chat_send(data->app, "ATE0 +CMEE=1", none_prefix,
NULL, NULL, NULL);
g_at_chat_send(data->mdm, "AT&C0", none_prefix, NULL, NULL, NULL);
g_at_chat_send(data->app, "AT&C0", none_prefix, NULL, NULL, NULL);
g_at_chat_send(data->app, "AT+CFUN=4", none_prefix,
cfun_enable, modem, NULL);
return -EINPROGRESS;
}
static void gemalto_smso_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_modem *modem = user_data;
struct gemalto_data *data = ofono_modem_get_data(modem);
DBG("");
g_at_chat_unref(data->mdm);
data->mdm = NULL;
g_at_chat_unref(data->app);
data->app = NULL;
if (ok)
ofono_modem_set_powered(modem, FALSE);
}
static int gemalto_disable(struct ofono_modem *modem)
{
struct gemalto_data *data = ofono_modem_get_data(modem);
DBG("%p", modem);
g_at_chat_cancel_all(data->app);
g_at_chat_unregister_all(data->app);
/* Shutdown the modem */
g_at_chat_send(data->app, "AT^SMSO", none_prefix, gemalto_smso_cb,
modem, NULL);
return -EINPROGRESS;
}
static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_modem_online_cb_t cb = cbd->cb;
struct ofono_error error;
decode_at_error(&error, g_at_result_final_response(result));
cb(&error, cbd->data);
}
static void gemalto_set_online(struct ofono_modem *modem, ofono_bool_t online,
ofono_modem_online_cb_t cb, void *user_data)
{
struct gemalto_data *data = ofono_modem_get_data(modem);
struct cb_data *cbd = cb_data_new(cb, user_data);
char const *command = online ? "AT+CFUN=1" : "AT+CFUN=4";
DBG("modem %p %s", modem, online ? "online" : "offline");
if (g_at_chat_send(data->app, command, NULL, set_online_cb, cbd, g_free))
return;
CALLBACK_WITH_FAILURE(cb, cbd->data);
g_free(cbd);
}
static void gemalto_pre_sim(struct ofono_modem *modem)
{
struct gemalto_data *data = ofono_modem_get_data(modem);
struct ofono_sim *sim;
DBG("%p", modem);
ofono_devinfo_create(modem, 0, "atmodem", data->app);
ofono_location_reporting_create(modem, 0, "gemaltomodem", data->app);
sim = ofono_sim_create(modem, 0, "atmodem", data->app);
if (sim && data->have_sim == TRUE)
ofono_sim_inserted_notify(sim, TRUE);
}
static void gemalto_post_sim(struct ofono_modem *modem)
{
struct gemalto_data *data = ofono_modem_get_data(modem);
struct ofono_gprs *gprs;
struct ofono_gprs_context *gc;
DBG("%p", modem);
ofono_phonebook_create(modem, 0, "atmodem", data->app);
ofono_sms_create(modem, OFONO_VENDOR_CINTERION, "atmodem", data->app);
gprs = ofono_gprs_create(modem, 0, "atmodem", data->app);
gc = ofono_gprs_context_create(modem, 0, "atmodem", data->mdm);
if (gprs && gc)
ofono_gprs_add_context(gprs, gc);
}
static void gemalto_post_online(struct ofono_modem *modem)
{
struct gemalto_data *data = ofono_modem_get_data(modem);
DBG("%p", modem);
ofono_netreg_create(modem, OFONO_VENDOR_CINTERION, "atmodem", data->app);
}
static struct ofono_modem_driver gemalto_driver = {
.name = "gemalto",
.probe = gemalto_probe,
.remove = gemalto_remove,
.enable = gemalto_enable,
.disable = gemalto_disable,
.set_online = gemalto_set_online,
.pre_sim = gemalto_pre_sim,
.post_sim = gemalto_post_sim,
.post_online = gemalto_post_online,
};
static int gemalto_init(void)
{
return ofono_modem_driver_register(&gemalto_driver);
}
static void gemalto_exit(void)
{
ofono_modem_driver_unregister(&gemalto_driver);
}
OFONO_PLUGIN_DEFINE(gemalto, "Gemalto modem plugin", VERSION,
OFONO_PLUGIN_PRIORITY_DEFAULT, gemalto_init, gemalto_exit)

View File

@@ -48,6 +48,7 @@
#include <drivers/qmimodem/qmi.h>
#include <drivers/qmimodem/dms.h>
#include <drivers/qmimodem/wda.h>
#include <drivers/qmimodem/util.h>
#define GOBI_DMS (1 << 0)
@@ -60,6 +61,7 @@
#define GOBI_CAT (1 << 7)
#define GOBI_CAT_OLD (1 << 8)
#define GOBI_VOICE (1 << 9)
#define GOBI_WDA (1 << 10)
struct gobi_data {
struct qmi_device *device;
@@ -168,6 +170,16 @@ static void get_oper_mode_cb(struct qmi_result *result, void *user_data)
data->oper_mode = mode;
/*
* Telit QMI LTE modem must remain online. If powered down, it also
* powers down the sim card, and QMI interface has no way to bring
* it back alive.
*/
if (ofono_modem_get_boolean(modem, "AlwaysOnline")) {
ofono_modem_set_powered(modem, TRUE);
return;
}
switch (data->oper_mode) {
case QMI_DMS_OPER_MODE_ONLINE:
param = qmi_param_new_uint8(QMI_DMS_PARAM_OPER_MODE,
@@ -250,7 +262,8 @@ static void discover_cb(uint8_t count, const struct qmi_version *list,
DBG("");
for (i = 0; i < count; i++) {
DBG("%s %d.%d", list[i].name, list[i].major, list[i].minor);
DBG("%s %d.%d - %d", list[i].name, list[i].major, list[i].minor,
list[i].type);
switch (list[i].type) {
case QMI_SERVICE_DMS:
@@ -265,6 +278,9 @@ static void discover_cb(uint8_t count, const struct qmi_version *list,
case QMI_SERVICE_WDS:
data->features |= GOBI_WDS;
break;
case QMI_SERVICE_WDA:
data->features |= GOBI_WDA;
break;
case QMI_SERVICE_PDS:
data->features |= GOBI_PDS;
break;
@@ -353,6 +369,14 @@ static int gobi_disable(struct ofono_modem *modem)
qmi_service_cancel_all(data->dms);
qmi_service_unregister_all(data->dms);
/*
* Telit QMI modem must remain online. If powered down, it also
* powers down the sim card, and QMI interface has no way to bring
* it back alive.
*/
if (ofono_modem_get_boolean(modem, "AlwaysOnline"))
goto out;
param = qmi_param_new_uint8(QMI_DMS_PARAM_OPER_MODE,
QMI_DMS_OPER_MODE_PERSIST_LOW_POWER);
if (!param)
@@ -362,6 +386,7 @@ static int gobi_disable(struct ofono_modem *modem)
power_disable_cb, modem, NULL) > 0)
return -EINPROGRESS;
out:
shutdown_device(modem);
return -EINPROGRESS;

View File

@@ -1,407 +0,0 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2008-2014 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <termios.h>
#include <string.h>
#include <sys/socket.h>
#include <glib.h>
#include <gatchat.h>
#include <gattty.h>
#define OFONO_API_SUBJECT_TO_CHANGE
#include <ofono/plugin.h>
#include <ofono/log.h>
#include <ofono/modem.h>
#include <ofono/call-barring.h>
#include <ofono/call-forwarding.h>
#include <ofono/call-meter.h>
#include <ofono/call-settings.h>
#include <ofono/devinfo.h>
#include <ofono/message-waiting.h>
#include <ofono/location-reporting.h>
#include <ofono/netreg.h>
#include <ofono/phonebook.h>
#include <ofono/sim.h>
#include <ofono/gprs.h>
#include <ofono/gprs-context.h>
#include <ofono/sms.h>
#include <ofono/ussd.h>
#include <ofono/voicecall.h>
#include <drivers/atmodem/atutil.h>
#include <drivers/atmodem/vendor.h>
static const char *none_prefix[] = { NULL };
static const char *qss_prefix[] = { "#QSS:", NULL };
struct he910_data {
GAtChat *chat; /* AT chat */
GAtChat *modem; /* Data port */
struct ofono_sim *sim;
ofono_bool_t have_sim;
ofono_bool_t sms_phonebook_added;
};
static void he910_debug(const char *str, void *user_data)
{
const char *prefix = user_data;
ofono_info("%s%s", prefix, str);
}
static GAtChat *open_device(struct ofono_modem *modem,
const char *key, char *debug)
{
const char *device;
GAtSyntax *syntax;
GIOChannel *channel;
GAtChat *chat;
GHashTable *options;
device = ofono_modem_get_string(modem, key);
if (device == NULL)
return NULL;
DBG("%s %s", key, device);
options = g_hash_table_new(g_str_hash, g_str_equal);
if (options == NULL)
return NULL;
g_hash_table_insert(options, "Baud", "115200");
channel = g_at_tty_open(device, options);
g_hash_table_destroy(options);
if (channel == NULL)
return NULL;
syntax = g_at_syntax_new_gsm_permissive();
chat = g_at_chat_new(channel, syntax);
g_at_syntax_unref(syntax);
g_io_channel_unref(channel);
if (chat == NULL)
return NULL;
if (getenv("OFONO_AT_DEBUG"))
g_at_chat_set_debug(chat, he910_debug, debug);
return chat;
}
static void switch_sim_state_status(struct ofono_modem *modem, int status)
{
struct he910_data *data = ofono_modem_get_data(modem);
DBG("%p, SIM status: %d", modem, status);
switch (status) {
case 0: /* SIM not inserted */
if (data->have_sim == TRUE) {
ofono_sim_inserted_notify(data->sim, FALSE);
data->have_sim = FALSE;
data->sms_phonebook_added = FALSE;
}
break;
case 1: /* SIM inserted */
case 2: /* SIM inserted and PIN unlocked */
if (data->have_sim == FALSE) {
ofono_sim_inserted_notify(data->sim, TRUE);
data->have_sim = TRUE;
}
break;
case 3: /* SIM inserted, SMS and phonebook ready */
if (data->sms_phonebook_added == FALSE) {
ofono_phonebook_create(modem, 0, "atmodem", data->chat);
ofono_sms_create(modem, 0, "atmodem", data->chat);
data->sms_phonebook_added = TRUE;
}
break;
default:
ofono_warn("Unknown SIM state %d received", status);
break;
}
}
static void he910_qss_notify(GAtResult *result, gpointer user_data)
{
struct ofono_modem *modem = user_data;
int status;
GAtResultIter iter;
DBG("%p", modem);
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "#QSS:"))
return;
g_at_result_iter_next_number(&iter, &status);
switch_sim_state_status(modem, status);
}
static void qss_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_modem *modem = user_data;
int status, mode;
GAtResultIter iter;
DBG("%p", modem);
if (!ok)
return;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "#QSS:"))
return;
if (!g_at_result_iter_next_number(&iter, &mode))
return;
if (!g_at_result_iter_next_number(&iter, &status))
return;
switch_sim_state_status(modem, status);
}
static void cfun_enable_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_modem *modem = user_data;
struct he910_data *data = ofono_modem_get_data(modem);
DBG("%p", modem);
if (!ok) {
g_at_chat_unref(data->chat);
data->chat = NULL;
g_at_chat_unref(data->modem);
data->modem = NULL;
ofono_modem_set_powered(modem, FALSE);
return;
}
/*
* Switch data carrier detect signal off.
* When the DCD is disabled the modem does not hangup anymore
* after the data connection.
*/
g_at_chat_send(data->chat, "AT&C0", NULL, NULL, NULL, NULL);
data->have_sim = FALSE;
data->sms_phonebook_added = FALSE;
ofono_modem_set_powered(modem, TRUE);
/*
* Tell the modem not to automatically initiate auto-attach
* proceedures on its own.
*/
g_at_chat_send(data->chat, "AT#AUTOATT=0", none_prefix,
NULL, NULL, NULL);
/* Follow sim state */
g_at_chat_register(data->chat, "#QSS:", he910_qss_notify,
FALSE, modem, NULL);
/* Enable sim state notification */
g_at_chat_send(data->chat, "AT#QSS=2", none_prefix, NULL, NULL, NULL);
g_at_chat_send(data->chat, "AT#QSS?", qss_prefix,
qss_query_cb, modem, NULL);
}
static int he910_enable(struct ofono_modem *modem)
{
struct he910_data *data = ofono_modem_get_data(modem);
DBG("%p", modem);
data->modem = open_device(modem, "Modem", "Modem: ");
if (data->modem == NULL)
return -EINVAL;
data->chat = open_device(modem, "Aux", "Aux: ");
if (data->chat == NULL) {
g_at_chat_unref(data->modem);
data->modem = NULL;
return -EIO;
}
g_at_chat_set_slave(data->modem, data->chat);
/*
* Disable command echo and
* enable the Extended Error Result Codes
*/
g_at_chat_send(data->chat, "ATE0 +CMEE=1", none_prefix,
NULL, NULL, NULL);
/* Set phone functionality */
g_at_chat_send(data->chat, "AT+CFUN=1", none_prefix,
cfun_enable_cb, modem, NULL);
return -EINPROGRESS;
}
static void cfun_disable_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_modem *modem = user_data;
struct he910_data *data = ofono_modem_get_data(modem);
DBG("%p", modem);
g_at_chat_unref(data->chat);
data->chat = NULL;
if (ok)
ofono_modem_set_powered(modem, FALSE);
}
static int he910_disable(struct ofono_modem *modem)
{
struct he910_data *data = ofono_modem_get_data(modem);
DBG("%p", modem);
g_at_chat_cancel_all(data->modem);
g_at_chat_unregister_all(data->modem);
g_at_chat_unref(data->modem);
data->modem = NULL;
g_at_chat_cancel_all(data->chat);
g_at_chat_unregister_all(data->chat);
/* Power down modem */
g_at_chat_send(data->chat, "AT+CFUN=4", none_prefix,
cfun_disable_cb, modem, NULL);
return -EINPROGRESS;
}
static void he910_pre_sim(struct ofono_modem *modem)
{
struct he910_data *data = ofono_modem_get_data(modem);
DBG("%p", modem);
ofono_devinfo_create(modem, 0, "atmodem", data->chat);
data->sim = ofono_sim_create(modem, OFONO_VENDOR_TELIT, "atmodem",
data->chat);
ofono_location_reporting_create(modem, 0, "telitmodem", data->chat);
}
static void he910_post_online(struct ofono_modem *modem)
{
struct he910_data *data = ofono_modem_get_data(modem);
struct ofono_message_waiting *mw;
struct ofono_gprs *gprs;
struct ofono_gprs_context *gc;
DBG("%p", modem);
ofono_voicecall_create(modem, 0, "atmodem", data->chat);
ofono_netreg_create(modem, OFONO_VENDOR_TELIT, "atmodem", data->chat);
ofono_ussd_create(modem, 0, "atmodem", data->chat);
ofono_call_forwarding_create(modem, 0, "atmodem", data->chat);
ofono_call_settings_create(modem, 0, "atmodem", data->chat);
ofono_call_meter_create(modem, 0, "atmodem", data->chat);
ofono_call_barring_create(modem, 0, "atmodem", data->chat);
mw = ofono_message_waiting_create(modem);
if (mw)
ofono_message_waiting_register(mw);
gprs = ofono_gprs_create(modem, OFONO_VENDOR_TELIT, "atmodem",
data->chat);
gc = ofono_gprs_context_create(modem, 0, "atmodem", data->modem);
if (gprs && gc)
ofono_gprs_add_context(gprs, gc);
}
static int he910_probe(struct ofono_modem *modem)
{
struct he910_data *data;
DBG("%p", modem);
data = g_try_new0(struct he910_data, 1);
if (data == NULL)
return -ENOMEM;
ofono_modem_set_data(modem, data);
return 0;
}
static void he910_remove(struct ofono_modem *modem)
{
struct he910_data *data = ofono_modem_get_data(modem);
DBG("%p", modem);
ofono_modem_set_data(modem, NULL);
/* Cleanup after hot-unplug */
g_at_chat_unref(data->chat);
g_at_chat_unref(data->modem);
g_free(data);
}
static struct ofono_modem_driver he910_driver = {
.name = "he910",
.probe = he910_probe,
.remove = he910_remove,
.enable = he910_enable,
.disable = he910_disable,
.pre_sim = he910_pre_sim,
.post_online = he910_post_online,
};
static int he910_init(void)
{
DBG("");
return ofono_modem_driver_register(&he910_driver);
}
static void he910_exit(void)
{
ofono_modem_driver_unregister(&he910_driver);
}
OFONO_PLUGIN_DEFINE(he910, "Telit HE910 driver", VERSION,
OFONO_PLUGIN_PRIORITY_DEFAULT, he910_init, he910_exit)

View File

@@ -177,11 +177,42 @@ static int service_level_connection(struct ofono_modem *modem,
return -EINPROGRESS;
}
static struct ofono_modem *modem_register(const char *device,
const char *device_address, const char *alias)
static void modem_removed(GDBusProxy *proxy, void *user_data)
{
struct ofono_modem *modem = user_data;
ofono_modem_remove(modem);
}
static void alias_changed(GDBusProxy *proxy, const char *name,
DBusMessageIter *iter, void *user_data)
{
const char *alias;
struct ofono_modem *modem = user_data;
if (g_str_equal("Alias", name) == FALSE)
return;
dbus_message_iter_get_basic(iter, &alias);
ofono_modem_set_name(modem, alias);
}
static struct ofono_modem *modem_register(const char *device, GDBusProxy *proxy)
{
struct ofono_modem *modem;
char *path;
DBusMessageIter iter;
const char *alias, *remote;
if (g_dbus_proxy_get_property(proxy, "Alias", &iter) == FALSE)
return NULL;
dbus_message_iter_get_basic(&iter, &alias);
if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
return NULL;
dbus_message_iter_get_basic(&iter, &remote);
path = g_strconcat("hfp", device, NULL);
@@ -192,12 +223,15 @@ static struct ofono_modem *modem_register(const char *device,
if (modem == NULL)
return NULL;
ofono_modem_set_string(modem, "Remote", device_address);
ofono_modem_set_string(modem, "Remote", remote);
ofono_modem_set_string(modem, "DevicePath", device);
ofono_modem_set_name(modem, alias);
ofono_modem_register(modem);
g_dbus_proxy_set_property_watch(proxy, alias_changed, modem);
g_dbus_proxy_set_removed_watch(proxy, modem_removed, modem);
return modem;
}
@@ -500,6 +534,71 @@ static int get_version(DBusMessageIter *iter, uint16_t *version)
return -ENOENT;
}
static gboolean has_hfp_ag_uuid(DBusMessageIter *array)
{
DBusMessageIter value;
if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
return FALSE;
dbus_message_iter_recurse(array, &value);
while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) {
const char *uuid;
dbus_message_iter_get_basic(&value, &uuid);
if (g_str_equal(uuid, HFP_AG_UUID) == TRUE)
return TRUE;
dbus_message_iter_next(&value);
}
return FALSE;
}
static void modem_unregister(struct ofono_modem *modem, GDBusProxy *proxy)
{
ofono_modem_remove(modem);
g_dbus_proxy_set_removed_watch(proxy, NULL, NULL);
g_dbus_proxy_set_property_watch(proxy, NULL, NULL);
}
static void *device_changed(GDBusProxy *proxy, const char *path)
{
DBusMessageIter iter;
dbus_bool_t paired;
struct ofono_modem *modem;
if (g_dbus_proxy_get_property(proxy, "Paired", &iter) == FALSE)
return NULL;
dbus_message_iter_get_basic(&iter, &paired);
modem = ofono_modem_find(device_path_compare, (void *) path);
if (paired == FALSE) {
if (modem != NULL)
modem_unregister(modem, proxy);
return NULL;
}
if (g_dbus_proxy_get_property(proxy, "UUIDs", &iter) == FALSE ||
has_hfp_ag_uuid(&iter) == FALSE) {
if (modem != NULL)
modem_unregister(modem, proxy);
return NULL;
}
/* Skip if modem already registered */
if (modem)
return modem;
modem = modem_register(path, proxy);
return modem;
}
static DBusMessage *profile_new_connection(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
@@ -542,10 +641,18 @@ static DBusMessage *profile_new_connection(DBusConnection *conn,
modem = ofono_modem_find(device_path_compare, (void *) device);
if (modem == NULL) {
close(fd);
return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE
".Rejected",
"Unknown Bluetooth device");
GDBusProxy *proxy;
proxy = g_dbus_proxy_new(bluez, device, BLUEZ_DEVICE_INTERFACE);
modem = modem_register(device, proxy);
g_dbus_proxy_unref(proxy);
if (!modem) {
close(fd);
return g_dbus_create_error(msg, BLUEZ_ERROR_INTERFACE
".Rejected",
"Unknown Bluetooth device");
}
}
err = service_level_connection(modem, fd, version);
@@ -687,93 +794,6 @@ static void connect_handler(DBusConnection *conn, void *user_data)
HFP_EXT_PROFILE_PATH, NULL, features);
}
static gboolean has_hfp_ag_uuid(DBusMessageIter *array)
{
DBusMessageIter value;
if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
return FALSE;
dbus_message_iter_recurse(array, &value);
while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) {
const char *uuid;
dbus_message_iter_get_basic(&value, &uuid);
if (g_str_equal(uuid, HFP_AG_UUID) == TRUE)
return TRUE;
dbus_message_iter_next(&value);
}
return FALSE;
}
static void modem_removed(GDBusProxy *proxy, void *user_data)
{
struct ofono_modem *modem = user_data;
ofono_modem_remove(modem);
}
static void alias_changed(GDBusProxy *proxy, const char *name,
DBusMessageIter *iter, void *user_data)
{
const char *alias;
struct ofono_modem *modem = user_data;
if (g_str_equal("Alias", name) == FALSE)
return;
dbus_message_iter_get_basic(iter, &alias);
ofono_modem_set_name(modem, alias);
}
static void modem_register_from_proxy(GDBusProxy *proxy, const char *path)
{
const char *alias, *remote;
DBusMessageIter iter;
dbus_bool_t paired;
struct ofono_modem *modem;
if (g_dbus_proxy_get_property(proxy, "Paired", &iter) == FALSE)
return;
dbus_message_iter_get_basic(&iter, &paired);
if (paired == FALSE) {
modem = ofono_modem_find(device_path_compare, (void *) path);
if (modem != NULL) {
ofono_modem_remove(modem);
g_dbus_proxy_set_removed_watch(proxy, NULL, NULL);
g_dbus_proxy_set_property_watch(proxy, NULL, NULL);
}
return;
}
if (g_dbus_proxy_get_property(proxy, "UUIDs", &iter) == FALSE)
return;
if (has_hfp_ag_uuid(&iter) == FALSE)
return;
if (g_dbus_proxy_get_property(proxy, "Alias", &iter) == FALSE)
return;
dbus_message_iter_get_basic(&iter, &alias);
if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
return;
dbus_message_iter_get_basic(&iter, &remote);
modem = modem_register(path, remote, alias);
g_dbus_proxy_set_property_watch(proxy, alias_changed, modem);
g_dbus_proxy_set_removed_watch(proxy, modem_removed, modem);
}
static void proxy_added(GDBusProxy *proxy, void *user_data)
{
const char *interface, *path;
@@ -784,7 +804,7 @@ static void proxy_added(GDBusProxy *proxy, void *user_data)
if (g_str_equal(BLUEZ_DEVICE_INTERFACE, interface) == FALSE)
return;
modem_register_from_proxy(proxy, path);
device_changed(proxy, path);
}
static void property_changed(GDBusProxy *proxy, const char *name,
@@ -798,10 +818,11 @@ static void property_changed(GDBusProxy *proxy, const char *name,
if (g_str_equal(BLUEZ_DEVICE_INTERFACE, interface) == FALSE)
return;
if (g_str_equal("Paired", name) != TRUE)
if (g_str_equal("Paired", name) != TRUE &&
g_str_equal("ServicesResolved", name) != TRUE)
return;
modem_register_from_proxy(proxy, path);
device_changed(proxy, path);
}
static int hfp_init(void)

View File

@@ -78,6 +78,8 @@ enum {
struct huawei_data {
GAtChat *modem;
GAtChat *pcui;
struct ofono_gprs *gprs;
struct ofono_gprs_context *gc;
gboolean have_sim;
int sim_state;
guint sysinfo_poll_source;
@@ -584,6 +586,48 @@ static GAtChat *open_device(struct ofono_modem *modem,
return chat;
}
static void modem_disconnect(gpointer user_data)
{
struct ofono_modem *modem = user_data;
struct huawei_data *data = ofono_modem_get_data(modem);
if (data == NULL) {
DBG("Modem has already been removed");
return;
}
ofono_warn("Modem channel disconnected");
/* clean and close modem device */
g_at_chat_cancel_all(data->modem);
g_at_chat_unregister_all(data->modem);
g_at_chat_unref(data->modem);
data->modem = NULL;
/* close gprs context driver */
ofono_gprs_context_remove(data->gc);
/* reopen modem channel */
data->modem = open_device(modem, "Modem", "Modem: ");
if (data->modem == NULL) {
DBG("Can't reopen device");
return;
}
/* configure modem channel */
g_at_chat_set_disconnect_function(data->modem, modem_disconnect, modem);
g_at_chat_set_slave(data->modem, data->pcui);
g_at_chat_send(data->modem, "ATE0 +CMEE=1", NULL, NULL, NULL, NULL);
/* reopen gprs context driver */
data->gc = ofono_gprs_context_create(modem, OFONO_VENDOR_HUAWEI,
"atmodem", data->modem);
if (data->gprs && data->gc)
ofono_gprs_add_context(data->gprs, data->gc);
}
static int huawei_enable(struct ofono_modem *modem)
{
struct huawei_data *data = ofono_modem_get_data(modem);
@@ -594,6 +638,8 @@ static int huawei_enable(struct ofono_modem *modem)
if (data->modem == NULL)
return -EINVAL;
g_at_chat_set_disconnect_function(data->modem, modem_disconnect, modem);
data->pcui = open_device(modem, "Pcui", "PCUI: ");
if (data->pcui == NULL) {
g_at_chat_unref(data->modem);
@@ -820,9 +866,6 @@ static void huawei_post_sim(struct ofono_modem *modem)
}
if (data->have_gsm == TRUE) {
struct ofono_gprs *gprs;
struct ofono_gprs_context *gc;
ofono_phonebook_create(modem, 0, "atmodem", data->pcui);
ofono_radio_settings_create(modem, 0,
"huaweimodem", data->pcui);
@@ -830,13 +873,13 @@ static void huawei_post_sim(struct ofono_modem *modem)
ofono_sms_create(modem, OFONO_VENDOR_HUAWEI,
"atmodem", data->pcui);
gprs = ofono_gprs_create(modem, OFONO_VENDOR_HUAWEI,
data->gprs = ofono_gprs_create(modem, OFONO_VENDOR_HUAWEI,
"atmodem", data->pcui);
gc = ofono_gprs_context_create(modem, 0,
data->gc = ofono_gprs_context_create(modem, 0,
"atmodem", data->modem);
if (gprs && gc)
ofono_gprs_add_context(gprs, gc);
if (data->gprs && data->gc)
ofono_gprs_add_context(data->gprs, data->gc);
}
}

View File

@@ -39,8 +39,15 @@
#include <ofono/gprs-provision.h>
#ifndef MBPI_DATABASE
#define MBPI_DATABASE "/usr/share/mobile-broadband-provider-info/" \
# ifdef PROVIDER_DATABASE
/* This one is pulled from mobile-broadband-provider-info.pc
* by the configure script, we should trust it. */
# define MBPI_DATABASE PROVIDER_DATABASE
# else
/* The default one */
# define MBPI_DATABASE "/usr/share/mobile-broadband-provider-info/" \
"serviceproviders.xml"
# endif
#endif
#include "mbpi.h"

View File

@@ -1,6 +1,6 @@
# do not edit this file, it will be overwritten on update
ACTION!="add|change", GOTO="ofono_end"
ACTION!="add", GOTO="ofono_end"
# ISI/Phonet drivers
SUBSYSTEM!="net", GOTO="ofono_isi_end"

View File

@@ -130,10 +130,15 @@ static void ril_radio_state_changed(struct ril_msg *message,
static int ril_probe(struct ofono_modem *modem)
{
struct ril_data *rd;
ofono_bool_t lte_cap;
DBG("");
rd = g_new0(struct ril_data, 1);
lte_cap = getenv("OFONO_RIL_RAT_LTE") ? TRUE : FALSE;
ofono_modem_set_boolean(modem, MODEM_PROP_LTE_CAPABLE, lte_cap);
ofono_modem_set_data(modem, rd);
return 0;
@@ -437,7 +442,10 @@ static void ril_post_sim(struct ofono_modem *modem)
struct ofono_gprs *gprs;
struct ofono_gprs_context *gc;
ofono_sms_create(modem, OFONO_RIL_VENDOR_IMC_SOFIA3GR,
if (ofono_modem_get_boolean(modem, MODEM_PROP_LTE_CAPABLE))
ofono_sms_create(modem, 0, "rilmodem", rd->ril);
else
ofono_sms_create(modem, OFONO_RIL_VENDOR_IMC_SOFIA3GR,
"rilmodem", rd->ril);
gprs = ofono_gprs_create(modem, 0, "rilmodem", rd->ril);
@@ -448,6 +456,11 @@ static void ril_post_sim(struct ofono_modem *modem)
OFONO_GPRS_CONTEXT_TYPE_INTERNET);
ofono_gprs_add_context(gprs, gc);
}
if (ofono_modem_get_boolean(modem, MODEM_PROP_LTE_CAPABLE))
ofono_lte_create(modem, "rilmodem", rd->ril);
ofono_stk_create(modem, 0, "rilmodem", rd->ril);
}
static void ril_post_online(struct ofono_modem *modem)
@@ -455,8 +468,13 @@ static void ril_post_online(struct ofono_modem *modem)
struct ril_data *rd = ofono_modem_get_data(modem);
ofono_netreg_create(modem, 0, "rilmodem", rd->ril);
ofono_radio_settings_create(modem, OFONO_RIL_VENDOR_IMC_SOFIA3GR,
"rilmodem", rd->ril);
if (ofono_modem_get_boolean(modem, MODEM_PROP_LTE_CAPABLE))
ofono_radio_settings_create(modem, 0, "rilmodem", rd->ril);
else
ofono_radio_settings_create(modem, OFONO_RIL_VENDOR_IMC_SOFIA3GR,
"rilmodem", rd->ril);
ofono_ussd_create(modem, 0, "rilmodem", rd->ril);
ofono_netmon_create(modem, 0, "rilmodem", rd->ril);
}
@@ -528,7 +546,7 @@ static int ril_enable(struct ofono_modem *modem)
g_ril_set_trace(rd->ril, TRUE);
if (getenv("OFONO_RIL_HEX_TRACE"))
g_ril_set_debugf(rd->ril, ril_debug, "Sofia3GR:");
g_ril_set_debugf(rd->ril, ril_debug, "IntelModem:");
g_ril_register(rd->ril, RIL_UNSOL_RIL_CONNECTED,
ril_connected, modem);
@@ -581,7 +599,7 @@ static int ril_disable(struct ofono_modem *modem)
}
static struct ofono_modem_driver ril_driver = {
.name = "ril_sofia3gr",
.name = "ril_intel",
.probe = ril_probe,
.remove = ril_remove,
.enable = ril_enable,
@@ -602,5 +620,5 @@ static void ril_exit(void)
ofono_modem_driver_unregister(&ril_driver);
}
OFONO_PLUGIN_DEFINE(ril_sofia3gr, "SoFiA 3GR RIL-based modem driver", VERSION,
OFONO_PLUGIN_DEFINE(ril_intel, "Intel RIL-based modem driver", VERSION,
OFONO_PLUGIN_PRIORITY_DEFAULT, ril_init, ril_exit)

View File

@@ -1,8 +1,7 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2015-2016 Jolla Ltd.
* Copyright (C) 2015-2017 Jolla Ltd.
* Contact: Slava Monich <slava.monich@jolla.com>
*
* This program is free software; you can redistribute it and/or modify
@@ -19,10 +18,7 @@
#include <config.h>
#endif
#define OFONO_API_SUBJECT_TO_CHANGE
#include <ofono/plugin.h>
#include <ofono/dbus.h>
#include <ofono/log.h>
#include "src/ofono.h"
#include <dbuslog_server_dbus.h>
#include <gutil_log.h>
@@ -113,13 +109,13 @@ static gboolean debuglog_match(const char* s1, const char* s2)
return s1 && s2 && !strcmp(s1, s2);
}
static void debuglog_update_flags(const char* name, guint set, guint clear)
static void debuglog_update_flags_range(struct ofono_debug_desc *start,
struct ofono_debug_desc *stop, const char* name,
guint set, guint clear)
{
const guint flags = set | clear;
struct ofono_debug_desc *start = __start___debug;
struct ofono_debug_desc *stop = __stop___debug;
if (start && stop) {
if (start && stop && start < stop) {
struct ofono_debug_desc *desc;
for (desc = start; desc < stop; desc++) {
@@ -142,7 +138,44 @@ static void debuglog_update_flags(const char* name, guint set, guint clear)
}
}
}
}
struct debuglog_update_flags_data {
const char* name;
guint set;
guint clear;
};
static void debuglog_update_flags_plugin(struct ofono_plugin_desc *desc,
int flags, void *user_data)
{
/*
* We are only interested in the external plugins here because
* they don't fall into __start___debug .. __stop___debug range.
*/
if (!(flags & OFONO_PLUGIN_FLAG_BUILTIN) &&
desc->debug_start && desc->debug_stop) {
const struct debuglog_update_flags_data *update = user_data;
debuglog_update_flags_range(desc->debug_start,
desc->debug_stop, update->name,
update->set, update->clear);
}
}
static void debuglog_update_flags(const char* name, guint set, guint clear)
{
struct debuglog_update_flags_data update;
/* Builtin plugins */
debuglog_update_flags_range(__start___debug, __stop___debug, name,
set, clear);
/* External plugins */
update.name = name;
update.set = set;
update.clear = clear;
__ofono_plugin_foreach(debuglog_update_flags_plugin, &update);
}
static void debuglog_category_enabled(DBusLogServer* server,
@@ -189,15 +222,10 @@ static guint debuglog_translate_flags(unsigned int ofono_flags)
return flags;
}
static int debuglog_init(void)
static void debuglog_add_categories(const struct ofono_debug_desc *start,
const struct ofono_debug_desc *stop)
{
const struct ofono_debug_desc *start = __start___debug;
const struct ofono_debug_desc *stop = __stop___debug;
debuglog_server = dbus_log_server_new(ofono_dbus_get_connection(),
DEBUGLOG_PATH);
if (start && stop) {
if (start && stop && start < stop) {
const struct ofono_debug_desc *desc;
GHashTable *hash = NULL;
@@ -221,19 +249,50 @@ static int debuglog_init(void)
g_hash_table_destroy(hash);
}
debuglog_event_id[DEBUG_EVENT_CATEGORY_ENABLED] =
dbus_log_server_add_category_enabled_handler(
debuglog_server, debuglog_category_enabled,
NULL);
debuglog_event_id[DEBUG_EVENT_CATEGORY_DISABLED] =
dbus_log_server_add_category_disabled_handler(
debuglog_server, debuglog_category_disabled,
NULL);
}
}
static void debuglog_add_external_plugin(struct ofono_plugin_desc *desc,
int flags, void *user_data)
{
/*
* We are only interested in the external plugins here because
* they don't fall into __start___debug .. __stop___debug range.
*/
if (!(flags & OFONO_PLUGIN_FLAG_BUILTIN)) {
if (desc->debug_start && desc->debug_stop) {
DBG("Adding \"%s\" plugin", desc->name);
debuglog_add_categories(desc->debug_start,
desc->debug_stop);
} else {
DBG("No debug descriptors for \"%s\" plugin",
desc->name);
}
}
}
static int debuglog_init(void)
{
debuglog_server = dbus_log_server_new(ofono_dbus_get_connection(),
DEBUGLOG_PATH);
/*
* First handle the executable and the builtin plugins (including
* this one) then the external plugins.
*/
debuglog_add_categories(__start___debug, __stop___debug);
__ofono_plugin_foreach(debuglog_add_external_plugin, NULL);
debuglog_event_id[DEBUG_EVENT_CATEGORY_ENABLED] =
dbus_log_server_add_category_enabled_handler(debuglog_server,
debuglog_category_enabled, NULL);
debuglog_event_id[DEBUG_EVENT_CATEGORY_DISABLED] =
dbus_log_server_add_category_disabled_handler(debuglog_server,
debuglog_category_disabled, NULL);
debuglog_default_log_proc = gutil_log_func2;
gutil_log_func2 = debuglog_gutil_log_func;
gutil_log_func = gutil_log_syslog;
ofono_log_hook = debuglog_ofono_log_hook;
dbus_log_server_set_default_level(debuglog_server, DBUSLOG_LEVEL_DEBUG);
@@ -251,5 +310,5 @@ static void debuglog_exit(void)
}
OFONO_PLUGIN_DEFINE(debuglog, "Debug log interface",
VERSION, OFONO_PLUGIN_PRIORITY_DEFAULT,
VERSION, OFONO_PLUGIN_PRIORITY_HIGH,
debuglog_init, debuglog_exit)

View File

@@ -0,0 +1,129 @@
/*
* 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_cell_info.h>
#include <gutil_log.h>
gint sailfish_cell_compare_location(const struct sailfish_cell *c1,
const struct sailfish_cell *c2)
{
if (c1 && c2) {
if (c1->type != c2->type) {
return c1->type - c2->type;
} else if (c1->type == SAILFISH_CELL_TYPE_GSM) {
const struct sailfish_cell_info_gsm *g1;
const struct sailfish_cell_info_gsm *g2;
g1 = &c1->info.gsm;
g2 = &c2->info.gsm;
if (g1->mcc != g2->mcc) {
return g1->mcc - g2->mcc;
} else if (g1->mnc != g2->mnc) {
return g1->mnc - g2->mnc;
} else if (g1->lac != g2->lac) {
return g1->lac - g2->lac;
} else {
return g1->cid - g2->cid;
}
} else if (c2->type == SAILFISH_CELL_TYPE_WCDMA) {
const struct sailfish_cell_info_wcdma *w1;
const struct sailfish_cell_info_wcdma *w2;
w1 = &c1->info.wcdma;
w2 = &c2->info.wcdma;
if (w1->mcc != w2->mcc) {
return w1->mcc - w2->mcc;
} else if (w1->mnc != w2->mnc) {
return w1->mnc - w2->mnc;
} else if (w1->lac != w2->lac) {
return w1->lac - w2->lac;
} else {
return w1->cid - w2->cid;
}
} else {
const struct sailfish_cell_info_lte *l1 =
&c1->info.lte;
const struct sailfish_cell_info_lte *l2 =
&c2->info.lte;
GASSERT(c1->type == SAILFISH_CELL_TYPE_LTE);
l1 = &c1->info.lte;
l2 = &c2->info.lte;
if (l1->mcc != l2->mcc) {
return l1->mcc - l2->mcc;
} else if (l1->mnc != l2->mnc) {
return l1->mnc - l2->mnc;
} else if (l1->ci != l2->ci) {
return l1->ci - l2->ci;
} else if (l1->pci != l2->pci) {
return l1->pci - l2->pci;
} else {
return l1->tac - l2->tac;
}
}
} else if (c1) {
return 1;
} else if (c2) {
return -1;
} else {
return 0;
}
}
gint sailfish_cell_compare_func(gconstpointer v1, gconstpointer v2)
{
return sailfish_cell_compare_location(v1, v2);
}
struct sailfish_cell_info *sailfish_cell_info_ref
(struct sailfish_cell_info *info)
{
if (info) {
info->proc->ref(info);
return info;
}
return NULL;
}
void sailfish_cell_info_unref(struct sailfish_cell_info *info)
{
if (info) {
info->proc->unref(info);
}
}
gulong sailfish_cell_info_add_cells_changed_handler
(struct sailfish_cell_info *info,
sailfish_cell_info_cb_t cb, void *arg)
{
return info ? info->proc->add_cells_changed_handler(info, cb, arg) : 0;
}
void sailfish_cell_info_remove_handler(struct sailfish_cell_info *info,
gulong id)
{
if (info) {
info->proc->remove_handler(info, id);
}
}
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -0,0 +1,598 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* 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.
*/
#include "sailfish_cell_info_dbus.h"
#include "sailfish_cell_info.h"
#include <ofono/modem.h>
#include <ofono/dbus.h>
#include <ofono/log.h>
#include <gdbus.h>
struct sailfish_cell_entry {
guint cell_id;
char *path;
struct sailfish_cell cell;
};
struct sailfish_cell_info_dbus {
struct sailfish_cell_info *info;
DBusConnection *conn;
char *path;
gulong handler_id;
guint next_cell_id;
GSList *entries;
};
#define CELL_INFO_DBUS_INTERFACE "org.nemomobile.ofono.CellInfo"
#define CELL_INFO_DBUS_CELLS_ADDED_SIGNAL "CellsAdded"
#define CELL_INFO_DBUS_CELLS_REMOVED_SIGNAL "CellsRemoved"
#define CELL_DBUS_INTERFACE_VERSION (1)
#define CELL_DBUS_INTERFACE "org.nemomobile.ofono.Cell"
#define CELL_DBUS_REGISTERED_CHANGED_SIGNAL "RegisteredChanged"
#define CELL_DBUS_PROPERTY_CHANGED_SIGNAL "PropertyChanged"
#define CELL_DBUS_REMOVED_SIGNAL "Removed"
struct sailfish_cell_property {
const char *name;
glong off;
int flag;
};
#define CELL_GSM_PROPERTY(value,name) \
{ #name, G_STRUCT_OFFSET(struct sailfish_cell_info_gsm,name), value }
#define CELL_WCDMA_PROPERTY(value,name) \
{ #name, G_STRUCT_OFFSET(struct sailfish_cell_info_wcdma,name), value }
#define CELL_LTE_PROPERTY(value,name) \
{ #name, G_STRUCT_OFFSET(struct sailfish_cell_info_lte,name), value }
static const struct sailfish_cell_property sailfish_cell_gsm_properties [] = {
CELL_GSM_PROPERTY(0x001,mcc),
CELL_GSM_PROPERTY(0x002,mnc),
CELL_GSM_PROPERTY(0x004,lac),
CELL_GSM_PROPERTY(0x008,cid),
CELL_GSM_PROPERTY(0x010,arfcn),
CELL_GSM_PROPERTY(0x020,bsic),
CELL_GSM_PROPERTY(0x040,signalStrength),
CELL_GSM_PROPERTY(0x080,bitErrorRate),
CELL_GSM_PROPERTY(0x100,timingAdvance)
};
static const struct sailfish_cell_property sailfish_cell_wcdma_properties [] = {
CELL_WCDMA_PROPERTY(0x01,mcc),
CELL_WCDMA_PROPERTY(0x02,mnc),
CELL_WCDMA_PROPERTY(0x04,lac),
CELL_WCDMA_PROPERTY(0x08,cid),
CELL_WCDMA_PROPERTY(0x10,psc),
CELL_WCDMA_PROPERTY(0x20,uarfcn),
CELL_WCDMA_PROPERTY(0x40,signalStrength),
CELL_WCDMA_PROPERTY(0x80,bitErrorRate)
};
static const struct sailfish_cell_property sailfish_cell_lte_properties [] = {
CELL_LTE_PROPERTY(0x001,mcc),
CELL_LTE_PROPERTY(0x002,mnc),
CELL_LTE_PROPERTY(0x004,ci),
CELL_LTE_PROPERTY(0x008,pci),
CELL_LTE_PROPERTY(0x010,tac),
CELL_LTE_PROPERTY(0x020,earfcn),
CELL_LTE_PROPERTY(0x040,signalStrength),
CELL_LTE_PROPERTY(0x080,rsrp),
CELL_LTE_PROPERTY(0x100,rsrq),
CELL_LTE_PROPERTY(0x200,rssnr),
CELL_LTE_PROPERTY(0x400,cqi),
CELL_LTE_PROPERTY(0x800,timingAdvance)
};
#define SAILFISH_CELL_PROPERTY_REGISTERED 0x1000
typedef void (*sailfish_cell_info_dbus_append_fn)(DBusMessageIter *it,
const struct sailfish_cell_entry *entry);
static const char *sailfish_cell_info_dbus_cell_type_str
(enum sailfish_cell_type type)
{
switch (type) {
case SAILFISH_CELL_TYPE_GSM:
return "gsm";
case SAILFISH_CELL_TYPE_WCDMA:
return "wcdma";
case SAILFISH_CELL_TYPE_LTE:
return "lte";
default:
return "unknown";
}
};
static const struct sailfish_cell_property *
sailfish_cell_info_dbus_cell_properties(
enum sailfish_cell_type type, int *count)
{
switch (type) {
case SAILFISH_CELL_TYPE_GSM:
*count = G_N_ELEMENTS(sailfish_cell_gsm_properties);
return sailfish_cell_gsm_properties;
case SAILFISH_CELL_TYPE_WCDMA:
*count = G_N_ELEMENTS(sailfish_cell_wcdma_properties);
return sailfish_cell_wcdma_properties;
case SAILFISH_CELL_TYPE_LTE:
*count = G_N_ELEMENTS(sailfish_cell_lte_properties);
return sailfish_cell_lte_properties;
default:
*count = 0;
return NULL;
}
};
static void sailfish_cell_info_destroy_entry(struct sailfish_cell_entry *entry)
{
if (entry) {
g_free(entry->path);
g_free(entry);
}
}
static DBusMessage *sailfish_cell_info_dbus_reply(DBusMessage *msg,
const struct sailfish_cell_entry *entry,
sailfish_cell_info_dbus_append_fn append)
{
DBusMessage *reply = dbus_message_new_method_return(msg);
DBusMessageIter it;
dbus_message_iter_init_append(reply, &it);
append(&it, entry);
return reply;
}
static void sailfish_cell_info_dbus_append_version(DBusMessageIter *it,
const struct sailfish_cell_entry *entry)
{
dbus_int32_t version = CELL_DBUS_INTERFACE_VERSION;
dbus_message_iter_append_basic(it, DBUS_TYPE_INT32, &version);
}
static void sailfish_cell_info_dbus_append_type(DBusMessageIter *it,
const struct sailfish_cell_entry *entry)
{
const char *type =
sailfish_cell_info_dbus_cell_type_str(entry->cell.type);
dbus_message_iter_append_basic(it, DBUS_TYPE_STRING, &type);
}
static void sailfish_cell_info_dbus_append_registered(DBusMessageIter *it,
const struct sailfish_cell_entry *entry)
{
dbus_bool_t registered = entry->cell.registered;
dbus_message_iter_append_basic(it, DBUS_TYPE_BOOLEAN, &registered);
}
static void sailfish_cell_info_dbus_append_properties(DBusMessageIter *it,
const struct sailfish_cell_entry *entry)
{
int i, n;
DBusMessageIter dict;
const struct sailfish_cell *cell = &entry->cell;
const struct sailfish_cell_property *prop =
sailfish_cell_info_dbus_cell_properties(cell->type, &n);
dbus_message_iter_open_container(it, DBUS_TYPE_ARRAY, "{sv}", &dict);
for (i = 0; i < n; i++) {
gint32 value = G_STRUCT_MEMBER(int, &cell->info, prop[i].off);
if (value != INT_MAX) {
ofono_dbus_dict_append(&dict, prop[i].name,
DBUS_TYPE_INT32, &value);
}
}
dbus_message_iter_close_container(it, &dict);
}
static void sailfish_cell_info_dbus_append_all(DBusMessageIter *it,
const struct sailfish_cell_entry *entry)
{
sailfish_cell_info_dbus_append_version(it, entry);
sailfish_cell_info_dbus_append_type(it, entry);
sailfish_cell_info_dbus_append_registered(it, entry);
sailfish_cell_info_dbus_append_properties(it, entry);
}
static DBusMessage *sailfish_cell_info_dbus_cell_get_all
(DBusConnection *conn, DBusMessage *msg, void *data)
{
return sailfish_cell_info_dbus_reply(msg, (struct sailfish_cell_entry*)
data, sailfish_cell_info_dbus_append_all);
}
static DBusMessage *sailfish_cell_info_dbus_cell_get_version
(DBusConnection *conn, DBusMessage *msg, void *data)
{
return sailfish_cell_info_dbus_reply(msg, (struct sailfish_cell_entry*)
data, sailfish_cell_info_dbus_append_version);
}
static DBusMessage *sailfish_cell_info_dbus_cell_get_type
(DBusConnection *conn, DBusMessage *msg, void *data)
{
return sailfish_cell_info_dbus_reply(msg, (struct sailfish_cell_entry*)
data, sailfish_cell_info_dbus_append_type);
}
static DBusMessage *sailfish_cell_info_dbus_cell_get_registered
(DBusConnection *conn, DBusMessage *msg, void *data)
{
return sailfish_cell_info_dbus_reply(msg, (struct sailfish_cell_entry*)
data, sailfish_cell_info_dbus_append_registered);
}
static DBusMessage *sailfish_cell_info_dbus_cell_get_properties
(DBusConnection *conn, DBusMessage *msg, void *data)
{
return sailfish_cell_info_dbus_reply(msg, (struct sailfish_cell_entry*)
data, sailfish_cell_info_dbus_append_properties);
}
static const GDBusMethodTable sailfish_cell_info_dbus_cell_methods[] = {
{ GDBUS_METHOD("GetAll", NULL,
GDBUS_ARGS({ "version", "i" },
{ "type", "s" },
{ "registered", "b" },
{ "properties", "a{sv}" }),
sailfish_cell_info_dbus_cell_get_all) },
{ GDBUS_METHOD("GetInterfaceVersion", NULL,
GDBUS_ARGS({ "version", "i" }),
sailfish_cell_info_dbus_cell_get_version) },
{ GDBUS_METHOD("GetType", NULL,
GDBUS_ARGS({ "type", "s" }),
sailfish_cell_info_dbus_cell_get_type) },
{ GDBUS_METHOD("GetRegistered", NULL,
GDBUS_ARGS({ "registered", "b" }),
sailfish_cell_info_dbus_cell_get_registered) },
{ GDBUS_METHOD("GetProperties", NULL,
GDBUS_ARGS({ "properties", "a{sv}" }),
sailfish_cell_info_dbus_cell_get_properties) },
{ }
};
static const GDBusSignalTable sailfish_cell_info_dbus_cell_signals[] = {
{ GDBUS_SIGNAL(CELL_DBUS_REGISTERED_CHANGED_SIGNAL,
GDBUS_ARGS({ "registered", "b" })) },
{ GDBUS_SIGNAL(CELL_DBUS_PROPERTY_CHANGED_SIGNAL,
GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
{ GDBUS_SIGNAL(CELL_DBUS_REMOVED_SIGNAL,
GDBUS_ARGS({})) },
{ }
};
static struct sailfish_cell_entry *sailfish_cell_info_dbus_find_id(
struct sailfish_cell_info_dbus *dbus, guint id)
{
GSList *l;
for (l = dbus->entries; l; l = l->next) {
struct sailfish_cell_entry *entry = l->data;
if (entry->cell_id == id) {
return entry;
}
}
return NULL;
}
static guint sailfish_cell_info_dbus_next_cell_id
(struct sailfish_cell_info_dbus *dbus)
{
while (sailfish_cell_info_dbus_find_id(dbus, dbus->next_cell_id)) {
dbus->next_cell_id++;
}
return dbus->next_cell_id++;
}
static struct sailfish_cell_entry *sailfish_cell_info_dbus_find_cell
(struct sailfish_cell_info_dbus *dbus,
const struct sailfish_cell *cell)
{
if (cell) {
GSList *l;
for (l = dbus->entries; l; l = l->next) {
struct sailfish_cell_entry *entry = l->data;
if (!sailfish_cell_compare_location(&entry->cell,
cell)) {
return entry;
}
}
}
return NULL;
}
static void sailfish_cell_info_dbus_emit_path_list
(struct sailfish_cell_info_dbus *dbus, const char *name,
GPtrArray *list)
{
guint i;
DBusMessageIter it, array;
DBusMessage *signal = dbus_message_new_signal(dbus->path,
CELL_INFO_DBUS_INTERFACE, name);
dbus_message_iter_init_append(signal, &it);
dbus_message_iter_open_container(&it, DBUS_TYPE_ARRAY, "o", &array);
for (i = 0; i < list->len; i++) {
const char* path = list->pdata[i];
dbus_message_iter_append_basic(&array, DBUS_TYPE_OBJECT_PATH,
&path);
}
dbus_message_iter_close_container(&it, &array);
g_dbus_send_message(dbus->conn, signal);
}
static int sailfish_cell_info_dbus_compare(const struct sailfish_cell *c1,
const struct sailfish_cell *c2)
{
if (c1->type == c2->type) {
int i, n, mask = 0;
const struct sailfish_cell_property *prop =
sailfish_cell_info_dbus_cell_properties(c1->type, &n);
if (c1->registered != c2->registered) {
mask |= SAILFISH_CELL_PROPERTY_REGISTERED;
}
for (i = 0; i < n; i++) {
const glong offset = prop[i].off;
gint32 v1 = G_STRUCT_MEMBER(int, &c1->info, offset);
gint32 v2 = G_STRUCT_MEMBER(int, &c2->info, offset);
if (v1 != v2) {
mask |= prop[i].flag;
}
}
return mask;
} else {
return -1;
}
}
static void sailfish_cell_info_dbus_property_changed
(struct sailfish_cell_info_dbus *dbus,
const struct sailfish_cell_entry *entry, int mask)
{
int i, n;
const struct sailfish_cell *cell = &entry->cell;
const struct sailfish_cell_property *prop =
sailfish_cell_info_dbus_cell_properties(cell->type, &n);
if (mask & SAILFISH_CELL_PROPERTY_REGISTERED) {
dbus_bool_t registered = cell->registered;
g_dbus_emit_signal(dbus->conn, entry->path,
CELL_DBUS_INTERFACE,
CELL_DBUS_REGISTERED_CHANGED_SIGNAL,
DBUS_TYPE_BOOLEAN, &registered, DBUS_TYPE_INVALID);
mask &= ~SAILFISH_CELL_PROPERTY_REGISTERED;
}
for (i = 0; i < n && mask; i++) {
if (mask & prop[i].flag) {
ofono_dbus_signal_property_changed(dbus->conn,
entry->path, CELL_DBUS_INTERFACE,
prop[i].name, DBUS_TYPE_INT32,
G_STRUCT_MEMBER_P(&cell->info, prop[i].off));
mask &= ~prop[i].flag;
}
}
}
static void sailfish_cell_info_dbus_update_entries
(struct sailfish_cell_info_dbus *dbus, gboolean emit_signals)
{
GSList *l;
GPtrArray* added = NULL;
GPtrArray* removed = NULL;
/* Remove non-existent cells */
l = dbus->entries;
while (l) {
GSList *next = l->next;
struct sailfish_cell_entry *entry = l->data;
if (!g_slist_find_custom(dbus->info->cells, &entry->cell,
sailfish_cell_compare_func)) {
DBG("%s removed", entry->path);
dbus->entries = g_slist_delete_link(dbus->entries, l);
g_dbus_emit_signal(dbus->conn, entry->path,
CELL_DBUS_INTERFACE,
CELL_DBUS_REMOVED_SIGNAL,
DBUS_TYPE_INVALID);
g_dbus_unregister_interface(dbus->conn, entry->path,
CELL_DBUS_INTERFACE);
if (emit_signals) {
if (!removed) {
removed =
g_ptr_array_new_with_free_func(
g_free);
}
/* Steal the path */
g_ptr_array_add(removed, entry->path);
entry->path = NULL;
}
sailfish_cell_info_destroy_entry(entry);
}
l = next;
}
/* Add new cells */
for (l = dbus->info->cells; l; l = l->next) {
const struct sailfish_cell *cell = l->data;
struct sailfish_cell_entry *entry =
sailfish_cell_info_dbus_find_cell(dbus, cell);
if (entry) {
if (emit_signals) {
int diff = sailfish_cell_info_dbus_compare(cell,
&entry->cell);
entry->cell = *cell;
sailfish_cell_info_dbus_property_changed(dbus,
entry, diff);
} else {
entry->cell = *cell;
}
} else {
entry = g_new0(struct sailfish_cell_entry, 1);
entry->cell = *cell;
entry->cell_id =
sailfish_cell_info_dbus_next_cell_id(dbus);
entry->path = g_strdup_printf("%s/cell_%u", dbus->path,
entry->cell_id);
dbus->entries = g_slist_append(dbus->entries, entry);
DBG("%s added", entry->path);
g_dbus_register_interface(dbus->conn, entry->path,
CELL_DBUS_INTERFACE,
sailfish_cell_info_dbus_cell_methods,
sailfish_cell_info_dbus_cell_signals, NULL,
entry, NULL);
if (emit_signals) {
if (!added) {
added = g_ptr_array_new();
}
g_ptr_array_add(added, entry->path);
}
}
}
if (removed) {
sailfish_cell_info_dbus_emit_path_list(dbus,
CELL_INFO_DBUS_CELLS_REMOVED_SIGNAL, removed);
g_ptr_array_free(removed, TRUE);
}
if (added) {
sailfish_cell_info_dbus_emit_path_list(dbus,
CELL_INFO_DBUS_CELLS_ADDED_SIGNAL, added);
g_ptr_array_free(added, TRUE);
}
}
static void sailfish_cell_info_dbus_cells_changed_cb
(struct sailfish_cell_info *info, void *arg)
{
DBG("");
sailfish_cell_info_dbus_update_entries
((struct sailfish_cell_info_dbus *)arg, TRUE);
}
static DBusMessage *sailfish_cell_info_dbus_get_cells(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct sailfish_cell_info_dbus *dbus = data;
DBusMessage *reply = dbus_message_new_method_return(msg);
DBusMessageIter it, array;
GSList *l;
dbus_message_iter_init_append(reply, &it);
dbus_message_iter_open_container(&it, DBUS_TYPE_ARRAY, "o", &array);
for (l = dbus->entries; l; l = l->next) {
const struct sailfish_cell_entry *entry = l->data;
dbus_message_iter_append_basic(&array, DBUS_TYPE_OBJECT_PATH,
&entry->path);
}
dbus_message_iter_close_container(&it, &array);
return reply;
}
static const GDBusMethodTable sailfish_cell_info_dbus_methods[] = {
{ GDBUS_METHOD("GetCells", NULL,
GDBUS_ARGS({ "paths", "ao" }),
sailfish_cell_info_dbus_get_cells) },
{ }
};
static const GDBusSignalTable sailfish_cell_info_dbus_signals[] = {
{ GDBUS_SIGNAL(CELL_INFO_DBUS_CELLS_ADDED_SIGNAL,
GDBUS_ARGS({ "paths", "ao" })) },
{ GDBUS_SIGNAL(CELL_INFO_DBUS_CELLS_REMOVED_SIGNAL,
GDBUS_ARGS({ "paths", "ao" })) },
{ }
};
struct sailfish_cell_info_dbus *sailfish_cell_info_dbus_new
(struct ofono_modem *modem, struct sailfish_cell_info *info)
{
if (modem && info) {
struct sailfish_cell_info_dbus *dbus =
g_new0(struct sailfish_cell_info_dbus, 1);
DBG("%s", ofono_modem_get_path(modem));
dbus->path = g_strdup(ofono_modem_get_path(modem));
dbus->conn = dbus_connection_ref(ofono_dbus_get_connection());
dbus->info = sailfish_cell_info_ref(info);
dbus->handler_id =
sailfish_cell_info_add_cells_changed_handler(info,
sailfish_cell_info_dbus_cells_changed_cb, dbus);
/* Register D-Bus interface */
if (g_dbus_register_interface(dbus->conn, dbus->path,
CELL_INFO_DBUS_INTERFACE,
sailfish_cell_info_dbus_methods,
sailfish_cell_info_dbus_signals,
NULL, dbus, NULL)) {
ofono_modem_add_interface(modem,
CELL_INFO_DBUS_INTERFACE);
sailfish_cell_info_dbus_update_entries(dbus, FALSE);
return dbus;
} else {
ofono_error("CellInfo D-Bus register failed");
sailfish_cell_info_dbus_free(dbus);
}
}
return NULL;
}
void sailfish_cell_info_dbus_free(struct sailfish_cell_info_dbus *dbus)
{
if (dbus) {
GSList *l;
DBG("%s", dbus->path);
g_dbus_unregister_interface(dbus->conn, dbus->path,
CELL_INFO_DBUS_INTERFACE);
/* Unregister cells */
l = dbus->entries;
while (l) {
struct sailfish_cell_entry *entry = l->data;
g_dbus_unregister_interface(dbus->conn, entry->path,
CELL_DBUS_INTERFACE);
sailfish_cell_info_destroy_entry(entry);
l = l->next;
}
g_slist_free(dbus->entries);
dbus_connection_unref(dbus->conn);
sailfish_cell_info_remove_handler(dbus->info, dbus->handler_id);
sailfish_cell_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

@@ -0,0 +1,36 @@
/*
* oFono - Open Source Telephony - RIL-based devices
*
* 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_CELL_INFO_DBUS_H
#define SAILFISH_CELL_INFO_DBUS_H
struct ofono_modem;
struct sailfish_cell_info;
struct sailfish_cell_info_dbus;
struct sailfish_cell_info_dbus *sailfish_cell_info_dbus_new
(struct ofono_modem *modem, struct sailfish_cell_info *info);
void sailfish_cell_info_dbus_free(struct sailfish_cell_info_dbus *dbus);
#endif /* SAILFISH_CELL_INFO_DBUS_H */
/*
* Local Variables:
* mode: C
* c-basic-offset: 8
* indent-tabs-mode: t
* End:
*/

View File

@@ -27,7 +27,10 @@
#include "storage.h"
#include <sailfish_manager.h>
#include <sailfish_cell_info.h>
#include "sailfish_manager_dbus.h"
#include "sailfish_cell_info_dbus.h"
#include "sailfish_sim_info.h"
#include "sailfish_watch.h"
@@ -84,6 +87,8 @@ struct sailfish_slot_priv {
struct sailfish_watch *watch;
struct sailfish_sim_info *siminfo;
struct sailfish_sim_info_dbus *siminfo_dbus;
struct sailfish_cell_info *cellinfo;
struct sailfish_cell_info_dbus *cellinfo_dbus;
enum sailfish_sim_state sim_state;
gulong watch_event_id[WATCH_EVENT_COUNT];
char *imei;
@@ -148,14 +153,33 @@ static void sailfish_manager_update_modem_paths_full
}
/*
* sailfish_manager_foreach_slot() terminates the loop and returns
* TRUE if the callback returns TRUE. If all callbacks return FALSE, it
* returns FALSE. It there are no slots, it returns FALSE too.
* sailfish_manager_foreach_driver() and sailfish_manager_foreach_slot()
* terminate the loop and return TRUE if the callback returns TRUE. If all
* callbacks return FALSE, they returns FALSE. It there are no drivers/slots,
* they return FALSE too.
*/
#define SF_LOOP_CONTINUE (FALSE)
#define SF_LOOP_DONE (TRUE)
static gboolean sailfish_manager_foreach_driver(struct sailfish_manager_priv *p,
gboolean (*fn)(struct sailfish_slot_driver_reg *r, void *user_data),
void *user_data)
{
struct sailfish_slot_driver_reg *r = p->drivers;
gboolean done = FALSE;
while (r && !done) {
struct sailfish_slot_driver_reg *rnext = r->next;
/* The callback returns TRUE to terminate the loop */
done = fn(r, user_data);
r = rnext;
}
return done;
}
static gboolean sailfish_manager_foreach_slot
(struct sailfish_manager_priv *p,
gboolean (*fn)(struct sailfish_slot_priv *s, void *user_data),
@@ -189,12 +213,31 @@ static gboolean sailfish_manager_foreach_slot
return done;
}
static void sailfish_manager_slot_update_cell_info_dbus
(struct sailfish_slot_priv *s)
{
struct ofono_modem *modem = s->watch->modem;
if (modem && s->cellinfo) {
if (!s->cellinfo_dbus) {
s->cellinfo_dbus = sailfish_cell_info_dbus_new(modem,
s->cellinfo);
}
} else {
if (s->cellinfo_dbus) {
sailfish_cell_info_dbus_free(s->cellinfo_dbus);
s->cellinfo_dbus = NULL;
}
}
}
static void sailfish_manager_slot_modem_changed(struct sailfish_watch *w,
void *user_data)
{
struct sailfish_slot_priv *s = user_data;
struct sailfish_manager_priv *p = s->manager->plugin;
sailfish_manager_slot_update_cell_info_dbus(s);
sailfish_manager_update_modem_paths_full(p);
sailfish_manager_update_ready(p);
}
@@ -382,6 +425,8 @@ static void sailfish_slot_free(struct sailfish_slot_priv *s)
}
sailfish_sim_info_unref(s->siminfo);
sailfish_sim_info_dbus_free(s->siminfo_dbus);
sailfish_cell_info_dbus_free(s->cellinfo_dbus);
sailfish_cell_info_unref(s->cellinfo);
sailfish_watch_remove_all_handlers(s->watch, s->watch_event_id);
sailfish_watch_unref(s->watch);
g_free(s->imei);
@@ -392,6 +437,55 @@ static void sailfish_slot_free(struct sailfish_slot_priv *s)
sailfish_manager_reindex_slots(p);
}
void sailfish_manager_set_cell_info(struct sailfish_slot *s,
struct sailfish_cell_info *info)
{
if (s) {
struct sailfish_slot_priv *slot = sailfish_slot_priv_cast(s);
if (slot->cellinfo != info) {
sailfish_cell_info_dbus_free(slot->cellinfo_dbus);
sailfish_cell_info_unref(slot->cellinfo);
slot->cellinfo = sailfish_cell_info_ref(info);
slot->cellinfo_dbus = NULL;
sailfish_manager_slot_update_cell_info_dbus(slot);
}
}
}
static gboolean sailfish_manager_update_dbus_block_proc
(struct sailfish_slot_driver_reg *r, void *data)
{
enum sailfish_manager_dbus_block *block = data;
struct sailfish_slot_manager *m;
struct sailfish_slot_priv *s;
if (r->init_id) {
/* Driver is being initialized */
(*block) |= SAILFISH_MANAGER_DBUS_BLOCK_ALL;
return SF_LOOP_DONE;
}
m = r->manager;
if (!m) {
return SF_LOOP_CONTINUE;
}
if (!m->started) {
/* Slots are being initialized */
(*block) |= SAILFISH_MANAGER_DBUS_BLOCK_ALL;
return SF_LOOP_DONE;
}
for (s = m->slots; s && s->imei; s = s->next);
if (s) {
/* IMEI is not available (yet) */
(*block) |= SAILFISH_MANAGER_DBUS_BLOCK_IMEI;
}
return SF_LOOP_CONTINUE;
}
static void sailfish_manager_update_dbus_block(struct sailfish_manager_priv *p)
{
enum sailfish_manager_dbus_block block =
@@ -401,35 +495,8 @@ static void sailfish_manager_update_dbus_block(struct sailfish_manager_priv *p)
/* Plugin is being initialized */
block |= SAILFISH_MANAGER_DBUS_BLOCK_ALL;
} else {
struct sailfish_slot_driver_reg *r;
for (r = p->drivers; r; r = r->next) {
struct sailfish_slot_manager *m;
struct sailfish_slot_priv *s;
if (r->init_id) {
/* Driver is being initialized */
block |= SAILFISH_MANAGER_DBUS_BLOCK_ALL;
break;
}
m = r->manager;
if (!m) {
continue;
}
if (!m->started) {
/* Slots are being initialized */
block |= SAILFISH_MANAGER_DBUS_BLOCK_ALL;
break;
}
for (s = m->slots; s && s->imei; s = s->next);
if (s) {
/* IMEI is not available (yet) */
block |= SAILFISH_MANAGER_DBUS_BLOCK_IMEI;
}
}
sailfish_manager_foreach_driver(p,
sailfish_manager_update_dbus_block_proc, &block);
}
sailfish_manager_dbus_set_block(p->dbus, block);
@@ -437,16 +504,14 @@ static void sailfish_manager_update_dbus_block(struct sailfish_manager_priv *p)
static void sailfish_manager_set_config_string
(struct sailfish_manager_priv *p, const char *key,
const char *value, gboolean sync)
const char *value)
{
if (value) {
g_key_file_set_string(p->storage, SF_STORE_GROUP, key, value);
} else {
g_key_file_remove_key(p->storage, SF_STORE_GROUP, key, NULL);
}
if (sync) {
storage_sync(NULL, SF_STORE, p->storage);
}
storage_sync(NULL, SF_STORE, p->storage);
}
struct sailfish_manager_slot_imsi_data {
@@ -640,8 +705,22 @@ static int sailfish_manager_update_modem_paths(struct sailfish_manager_priv *p)
return mask;
}
static gboolean sailfish_manager_update_ready_proc
(struct sailfish_slot_priv *s, void *unused)
static gboolean sailfish_manager_update_ready_driver_proc
(struct sailfish_slot_driver_reg *r, void *unused)
{
struct sailfish_slot_manager *m = r->manager;
if (!m || m->started) {
/* This one is either missing or ready */
return SF_LOOP_CONTINUE;
} else {
/* This one is not */
return SF_LOOP_DONE;
}
}
static gboolean sailfish_manager_update_ready_slot_proc
(struct sailfish_slot_priv *s, void *unused)
{
if (s->imei && s->sim_state != SAILFISH_SIM_STATE_UNKNOWN) {
/* This one is ready */
@@ -655,11 +734,14 @@ static gboolean sailfish_manager_update_ready_proc
static gboolean sailfish_manager_update_ready(struct sailfish_manager_priv *p)
{
/*
* sailfish_manager_foreach_slot() returns FALSE if either all
* callbacks returned FALSE (SF_LOOP_CONTINUE) or there are no
* slots. In either case we are ready. */
const gboolean ready = !sailfish_manager_foreach_slot(p,
sailfish_manager_update_ready_proc, NULL);
* sailfish_manager_foreach_driver and sailfish_manager_foreach_slot
* return FALSE if either all callbacks returned SF_LOOP_CONTINUE or
* there are no drivers/slots. In either case we are ready. */
const gboolean ready =
!sailfish_manager_foreach_driver
(p,sailfish_manager_update_ready_driver_proc, NULL) &&
!sailfish_manager_foreach_slot
(p, sailfish_manager_update_ready_slot_proc, NULL);
if (p->pub.ready != ready) {
p->pub.ready = ready;
@@ -827,7 +909,7 @@ static void sailfish_manager_set_enabled_slots(struct sailfish_manager *m,
* default behavior. */
if (data.all_enabled) {
sailfish_manager_set_config_string(p,
SF_STORE_ENABLED_SLOTS, NULL, TRUE);
SF_STORE_ENABLED_SLOTS, NULL);
} else {
const char *value;
char *tmp;
@@ -841,7 +923,7 @@ static void sailfish_manager_set_enabled_slots(struct sailfish_manager *m,
}
sailfish_manager_set_config_string(p,
SF_STORE_ENABLED_SLOTS, value, TRUE);
SF_STORE_ENABLED_SLOTS, value);
g_free(tmp);
}
g_strfreev(new_slots);
@@ -864,7 +946,7 @@ static void sailfish_manager_set_default_voice_imsi(struct sailfish_manager *m,
m->default_voice_imsi =
p->default_voice_imsi = g_strdup(imsi);
sailfish_manager_set_config_string(p,
SF_STORE_DEFAULT_VOICE_SIM, imsi, TRUE);
SF_STORE_DEFAULT_VOICE_SIM, imsi);
sailfish_manager_dbus_signal(p->dbus,
SAILFISH_MANAGER_SIGNAL_VOICE_IMSI |
sailfish_manager_update_modem_paths(p));
@@ -882,7 +964,7 @@ static void sailfish_manager_set_default_data_imsi(struct sailfish_manager *m,
m->default_data_imsi =
p->default_data_imsi = g_strdup(imsi);
sailfish_manager_set_config_string(p,
SF_STORE_DEFAULT_DATA_SIM, imsi, TRUE);
SF_STORE_DEFAULT_DATA_SIM, imsi);
sailfish_manager_dbus_signal(p->dbus,
SAILFISH_MANAGER_SIGNAL_DATA_IMSI |
sailfish_manager_update_modem_paths(p));
@@ -964,6 +1046,7 @@ void sailfish_manager_slot_error(struct sailfish_slot *s, const char *key,
struct sailfish_slot_priv *priv = sailfish_slot_priv_cast(s);
/* slot->path always starts with a slash, skip it */
const char *section = s->path + 1;
priv->errors = sailfish_manager_inc_error_count(priv->errors,
section, key);
sailfish_manager_dbus_signal_modem_error
@@ -1154,7 +1237,9 @@ static gboolean sailfish_manager_priv_init(gpointer user_data)
if (!p->init_countdown) {
p->init_id = 0;
DBG("done with registrations");
sailfish_manager_update_dbus_block(p);
if (!sailfish_manager_update_ready(p)) {
sailfish_manager_update_dbus_block(p);
}
return G_SOURCE_REMOVE;
} else {
/* Keep on waiting */

View File

@@ -2,7 +2,7 @@
*
* oFono - Open Source Telephony
*
* Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
* Copyright (C) 2008-2014 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
@@ -46,6 +46,7 @@
#include <ofono/call-settings.h>
#include <ofono/devinfo.h>
#include <ofono/message-waiting.h>
#include <ofono/location-reporting.h>
#include <ofono/netreg.h>
#include <ofono/phonebook.h>
#include <ofono/sim.h>
@@ -58,10 +59,41 @@
#include <drivers/atmodem/atutil.h>
#include <drivers/atmodem/vendor.h>
#include "bluez4.h"
static const char *none_prefix[] = { NULL };
static const char *rsen_prefix[]= { "#RSEN:", NULL };
static const char *qss_prefix[] = { "#QSS:", NULL };
enum modem_model {
HE910 = 1,
UE910,
LE910,
UC864,
UE866,
};
static struct {
enum modem_model model;
const char *variant;
gboolean has_voice;
gboolean has_gps;
} variants_list[] = {
{ HE910, NULL, FALSE, FALSE },
{ HE910, "G", TRUE, TRUE },
{ HE910, "GL", TRUE, FALSE },
{ HE910, "EUR", TRUE, FALSE },
{ HE910, "NAR", TRUE, FALSE },
{ HE910, "DG", FALSE, TRUE },
{ HE910, "EUG", FALSE, TRUE },
{ HE910, "NAG", FALSE, TRUE },
{ UE910, NULL, FALSE, FALSE },
{ UE910, "EUR", TRUE, FALSE },
{ UE910, "NAR", TRUE, FALSE },
{ LE910, NULL, FALSE, FALSE },
{ UC864, NULL, TRUE, FALSE },
{ UC864, "G", TRUE, TRUE },
{ UC864, "WD", FALSE, FALSE },
{ UE866, NULL, FALSE, FALSE },
{ }
};
struct telit_data {
GAtChat *chat; /* AT chat */
@@ -69,11 +101,9 @@ struct telit_data {
struct ofono_sim *sim;
ofono_bool_t have_sim;
ofono_bool_t sms_phonebook_added;
struct ofono_modem *sap_modem;
GIOChannel *bt_io;
GIOChannel *hw_io;
guint bt_watch;
guint hw_watch;
enum modem_model model;
gboolean has_voice;
gboolean has_gps;
};
static void telit_debug(const char *str, void *user_data)
@@ -83,102 +113,6 @@ static void telit_debug(const char *str, void *user_data)
ofono_info("%s%s", prefix, str);
}
static void sap_close_io(struct ofono_modem *modem)
{
struct telit_data *data = ofono_modem_get_data(modem);
if (data->bt_io != NULL) {
int sk = g_io_channel_unix_get_fd(data->bt_io);
shutdown(sk, SHUT_RDWR);
g_io_channel_unref(data->bt_io);
data->bt_io = NULL;
}
if (data->bt_watch > 0)
g_source_remove(data->bt_watch);
g_io_channel_unref(data->hw_io);
data->hw_io = NULL;
if (data->hw_watch > 0)
g_source_remove(data->hw_watch);
}
static void bt_watch_remove(gpointer userdata)
{
struct ofono_modem *modem = userdata;
struct telit_data *data = ofono_modem_get_data(modem);
ofono_modem_set_powered(modem, FALSE);
data->bt_watch = 0;
}
static gboolean bt_event_cb(GIOChannel *bt_io, GIOCondition condition,
gpointer userdata)
{
struct ofono_modem *modem = userdata;
struct telit_data *data = ofono_modem_get_data(modem);
if (condition & G_IO_IN) {
GIOStatus status;
gsize bytes_read, bytes_written;
gchar buf[300];
status = g_io_channel_read_chars(bt_io, buf, 300,
&bytes_read, NULL);
if (bytes_read > 0)
g_io_channel_write_chars(data->hw_io, buf,
bytes_read, &bytes_written, NULL);
if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN)
return FALSE;
return TRUE;
}
return FALSE;
}
static void hw_watch_remove(gpointer userdata)
{
struct ofono_modem *modem = userdata;
struct telit_data *data = ofono_modem_get_data(modem);
ofono_modem_set_powered(modem, FALSE);
data->hw_watch = 0;
}
static gboolean hw_event_cb(GIOChannel *hw_io, GIOCondition condition,
gpointer userdata)
{
struct ofono_modem *modem = userdata;
struct telit_data *data = ofono_modem_get_data(modem);
if (condition & G_IO_IN) {
GIOStatus status;
gsize bytes_read, bytes_written;
gchar buf[300];
status = g_io_channel_read_chars(hw_io, buf, 300,
&bytes_read, NULL);
if (bytes_read > 0)
g_io_channel_write_chars(data->bt_io, buf,
bytes_read, &bytes_written, NULL);
if (status != G_IO_STATUS_NORMAL && status != G_IO_STATUS_AGAIN)
return FALSE;
return TRUE;
}
return FALSE;
}
static GAtChat *open_device(struct ofono_modem *modem,
const char *key, char *debug)
{
@@ -199,15 +133,13 @@ static GAtChat *open_device(struct ofono_modem *modem,
return NULL;
g_hash_table_insert(options, "Baud", "115200");
channel = g_at_tty_open(device, options);
g_hash_table_destroy(options);
if (channel == NULL)
return NULL;
syntax = g_at_syntax_new_gsmv1();
syntax = g_at_syntax_new_gsm_permissive();
chat = g_at_chat_new(channel, syntax);
g_at_syntax_unref(syntax);
g_io_channel_unref(channel);
@@ -243,6 +175,11 @@ static void switch_sim_state_status(struct ofono_modem *modem, int status)
}
break;
case 3: /* SIM inserted, SMS and phonebook ready */
if (data->have_sim == FALSE) {
ofono_sim_inserted_notify(data->sim, TRUE);
data->have_sim = TRUE;
}
if (data->sms_phonebook_added == FALSE) {
ofono_phonebook_create(modem, 0, "atmodem", data->chat);
ofono_sms_create(modem, 0, "atmodem", data->chat);
@@ -273,33 +210,61 @@ static void telit_qss_notify(GAtResult *result, gpointer user_data)
switch_sim_state_status(modem, status);
}
static void qss_query_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_modem *modem = user_data;
int status, mode;
GAtResultIter iter;
DBG("%p", modem);
if (!ok)
return;
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "#QSS:"))
return;
if (!g_at_result_iter_next_number(&iter, &mode))
return;
if (!g_at_result_iter_next_number(&iter, &status))
return;
switch_sim_state_status(modem, status);
}
static void cfun_enable_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_modem *modem = user_data;
struct telit_data *data = ofono_modem_get_data(modem);
struct ofono_modem *m = data->sap_modem ? : modem;
DBG("%p", modem);
if (!ok) {
g_at_chat_unref(data->chat);
data->chat = NULL;
ofono_modem_set_powered(m, FALSE);
sap_close_io(modem);
g_at_chat_unref(data->modem);
data->modem = NULL;
ofono_modem_set_powered(modem, FALSE);
return;
}
/*
* Switch data carrier detect signal off.
* When the DCD is disabled the modem does not hangup anymore
* after the data connection.
* after the data connection. We need to do that on both channels.
*/
g_at_chat_send(data->chat, "AT&C0", NULL, NULL, NULL, NULL);
g_at_chat_send(data->modem, "AT&C0", NULL, NULL, NULL, NULL);
data->have_sim = FALSE;
data->sms_phonebook_added = FALSE;
ofono_modem_set_powered(m, TRUE);
ofono_modem_set_powered(modem, TRUE);
/*
* Tell the modem not to automatically initiate auto-attach
@@ -314,6 +279,105 @@ static void cfun_enable_cb(gboolean ok, GAtResult *result, gpointer user_data)
/* Enable sim state notification */
g_at_chat_send(data->chat, "AT#QSS=2", none_prefix, NULL, NULL, NULL);
g_at_chat_send(data->chat, "AT#QSS?", qss_prefix,
qss_query_cb, modem, NULL);
}
static gboolean find_model_variant(struct ofono_modem *modem,
const char * model_variant)
{
struct telit_data *data = ofono_modem_get_data(modem);
char model[32];
char variant[32];
gchar **tokens;
int i;
if (!model_variant || model_variant[0] == '\0')
return FALSE;
DBG("%s", model_variant);
tokens = g_strsplit(model_variant, "-", 2);
if (!tokens || !tokens[0] || !tokens[1])
return FALSE;
g_strlcpy(model, tokens[0], sizeof(model));
g_strlcpy(variant, tokens[1], sizeof(variant));
g_strfreev(tokens);
if (g_str_equal(model, "HE910"))
data->model = HE910;
else if (g_str_equal(model, "UE910"))
data->model = UE910;
else if (g_str_equal(model, "LE910"))
data->model = LE910;
else if (g_str_equal(model, "UC864"))
data->model = UC864;
else if (g_str_equal(model, "UE866"))
data->model = UE866;
else
return FALSE;
DBG("Model: %s", model);
for (i = 0; variants_list[i].model; i++) {
if (variants_list[i].model != data->model)
continue;
/* Set model defaults */
if (variants_list[i].variant == NULL) {
data->has_voice = variants_list[i].has_voice;
data->has_gps = variants_list[i].has_gps;
continue;
}
/* Specific variant match */
if (g_str_equal(variant, variants_list[i].variant)) {
DBG("Variant: %s", variant);
data->has_voice = variants_list[i].has_voice;
data->has_gps = variants_list[i].has_gps;
}
}
return TRUE;
}
static void cfun_gmm_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_modem *modem = user_data;
struct telit_data *data = ofono_modem_get_data(modem);
const char * model_variant;
DBG("%p", modem);
if (!ok)
goto error;
/* Get +GMM response */
if (!at_util_parse_attr(result, "", &model_variant))
goto error;
/* Try to find modem model and variant */
if (!find_model_variant(modem, model_variant)) {
ofono_info("Unknown xE910 model/variant %s", model_variant);
goto error;
}
/* Set phone functionality */
if (g_at_chat_send(data->chat, "AT+CFUN=1", none_prefix,
cfun_enable_cb, modem, NULL) > 0)
return;
error:
g_at_chat_unref(data->chat);
data->chat = NULL;
g_at_chat_unref(data->modem);
data->modem = NULL;
ofono_modem_set_powered(modem, FALSE);
}
static int telit_enable(struct ofono_modem *modem)
@@ -341,67 +405,23 @@ static int telit_enable(struct ofono_modem *modem)
*/
g_at_chat_send(data->chat, "ATE0 +CMEE=1", none_prefix,
NULL, NULL, NULL);
g_at_chat_send(data->modem, "ATE0", none_prefix,
NULL, NULL, NULL);
/*
* Disable sim state notification so that we sure get a notification
* when we enable it again later and don't have to query it.
*/
g_at_chat_send(data->chat, "AT#QSS=0", none_prefix, NULL, NULL, NULL);
/* Set phone functionality */
g_at_chat_send(data->chat, "AT+CFUN=4", none_prefix,
cfun_enable_cb, modem, NULL);
/* Get modem model and variant */
g_at_chat_send(data->chat, "AT+GMM", NULL,
cfun_gmm_cb, modem, NULL);
return -EINPROGRESS;
}
static void telit_rsen_notify(GAtResult *result, gpointer user_data)
{
struct ofono_modem *modem = user_data;
struct telit_data *data = ofono_modem_get_data(modem);
int status;
GAtResultIter iter;
DBG("%p", modem);
g_at_result_iter_init(&iter, result);
if (!g_at_result_iter_next(&iter, "#RSEN:"))
return;
g_at_result_iter_next_number(&iter, &status);
if (status == 0) {
ofono_modem_set_powered(data->sap_modem, FALSE);
sap_close_io(modem);
return;
}
telit_enable(modem);
}
static void rsen_enable_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_modem *modem = user_data;
struct telit_data *data = ofono_modem_get_data(modem);
DBG("%p", modem);
if (!ok) {
ofono_modem_set_powered(data->sap_modem, FALSE);
sap_close_io(modem);
return;
}
}
static void cfun_disable_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_modem *modem = user_data;
struct telit_data *data = ofono_modem_get_data(modem);
if(data->sap_modem)
modem = data->sap_modem;
DBG("%p", modem);
g_at_chat_unref(data->chat);
@@ -409,13 +429,12 @@ static void cfun_disable_cb(gboolean ok, GAtResult *result, gpointer user_data)
if (ok)
ofono_modem_set_powered(modem, FALSE);
data->sap_modem = NULL;
}
static int telit_disable(struct ofono_modem *modem)
{
struct telit_data *data = ofono_modem_get_data(modem);
DBG("%p", modem);
g_at_chat_cancel_all(data->modem);
@@ -427,211 +446,66 @@ static int telit_disable(struct ofono_modem *modem)
g_at_chat_unregister_all(data->chat);
/* Power down modem */
g_at_chat_send(data->chat, "AT+CFUN=0", none_prefix,
g_at_chat_send(data->chat, "AT+CFUN=4", none_prefix,
cfun_disable_cb, modem, NULL);
return -EINPROGRESS;
}
static void rsen_disable_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct ofono_modem *modem = user_data;
DBG("%p", modem);
sap_close_io(modem);
telit_disable(modem);
}
static int telit_sap_open(void)
{
const char *device = "/dev/ttyUSB4";
struct termios ti;
int fd;
DBG("%s", device);
fd = open(device, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (fd < 0)
return -EINVAL;
/* Switch TTY to raw mode */
memset(&ti, 0, sizeof(ti));
cfmakeraw(&ti);
ti.c_cflag |= (B115200 | CLOCAL | CREAD);
tcflush(fd, TCIOFLUSH);
if (tcsetattr(fd, TCSANOW, &ti) < 0) {
close(fd);
return -EBADF;
}
return fd;
}
static int telit_sap_enable(struct ofono_modem *modem,
struct ofono_modem *sap_modem,
int bt_fd)
{
struct telit_data *data = ofono_modem_get_data(modem);
int fd;
DBG("%p", modem);
fd = telit_sap_open();
if (fd < 0)
goto error;
data->hw_io = g_io_channel_unix_new(fd);
if (data->hw_io == NULL) {
close(fd);
goto error;
}
g_io_channel_set_encoding(data->hw_io, NULL, NULL);
g_io_channel_set_buffered(data->hw_io, FALSE);
g_io_channel_set_close_on_unref(data->hw_io, TRUE);
data->bt_io = g_io_channel_unix_new(bt_fd);
if (data->bt_io == NULL)
goto error;
g_io_channel_set_encoding(data->bt_io, NULL, NULL);
g_io_channel_set_buffered(data->bt_io, FALSE);
g_io_channel_set_close_on_unref(data->bt_io, TRUE);
data->hw_watch = g_io_add_watch_full(data->hw_io, G_PRIORITY_DEFAULT,
G_IO_HUP | G_IO_ERR | G_IO_NVAL | G_IO_IN,
hw_event_cb, modem, hw_watch_remove);
data->bt_watch = g_io_add_watch_full(data->bt_io, G_PRIORITY_DEFAULT,
G_IO_HUP | G_IO_ERR | G_IO_NVAL | G_IO_IN,
bt_event_cb, modem, bt_watch_remove);
data->sap_modem = sap_modem;
g_at_chat_register(data->chat, "#RSEN:", telit_rsen_notify,
FALSE, modem, NULL);
g_at_chat_send(data->chat, "AT#NOPT=0", NULL, NULL, NULL, NULL);
/* Set SAP functionality */
g_at_chat_send(data->chat, "AT#RSEN=1,1,0,2,0", rsen_prefix,
rsen_enable_cb, modem, NULL);
return -EINPROGRESS;
error:
shutdown(bt_fd, SHUT_RDWR);
close(bt_fd);
sap_close_io(modem);
return -EINVAL;
}
static int telit_sap_disable(struct ofono_modem *modem)
{
struct telit_data *data = ofono_modem_get_data(modem);
DBG("%p", modem);
g_at_chat_send(data->chat, "AT#RSEN=0", rsen_prefix,
rsen_disable_cb, modem, NULL);
return -EINPROGRESS;
}
static void telit_pre_sim(struct ofono_modem *modem)
{
struct telit_data *data = ofono_modem_get_data(modem);
if (data->sap_modem)
modem = data->sap_modem;
DBG("%p", modem);
ofono_devinfo_create(modem, 0, "atmodem", data->chat);
data->sim = ofono_sim_create(modem, OFONO_VENDOR_TELIT, "atmodem",
data->chat);
ofono_voicecall_create(modem, 0, "atmodem", data->chat);
}
static void telit_post_sim(struct ofono_modem *modem)
{
struct telit_data *data = ofono_modem_get_data(modem);
struct ofono_gprs *gprs;
struct ofono_gprs_context *gc;
if (data->sap_modem)
modem = data->sap_modem;
DBG("%p", modem);
gprs = ofono_gprs_create(modem, OFONO_VENDOR_TELIT, "atmodem",
data->chat);
gc = ofono_gprs_context_create(modem, 0, "atmodem", data->modem);
if (gprs && gc)
ofono_gprs_add_context(gprs, gc);
}
static void set_online_cb(gboolean ok, GAtResult *result, gpointer user_data)
{
struct cb_data *cbd = user_data;
ofono_modem_online_cb_t cb = cbd->cb;
struct ofono_error error;
decode_at_error(&error, g_at_result_final_response(result));
cb(&error, cbd->data);
}
static void telit_set_online(struct ofono_modem *modem, ofono_bool_t online,
ofono_modem_online_cb_t cb, void *user_data)
{
struct telit_data *data = ofono_modem_get_data(modem);
struct cb_data *cbd = cb_data_new(cb, user_data);
char const *command = online ? "AT+CFUN=1,0" : "AT+CFUN=4,0";
DBG("modem %p %s", modem, online ? "online" : "offline");
g_at_chat_send(data->chat, command, none_prefix, set_online_cb,
cbd, g_free);
if (data->has_gps)
ofono_location_reporting_create(modem, 0, "telitmodem",
data->chat);
}
static void telit_post_online(struct ofono_modem *modem)
{
struct telit_data *data = ofono_modem_get_data(modem);
struct ofono_message_waiting *mw;
if(data->sap_modem)
modem = data->sap_modem;
struct ofono_gprs *gprs;
struct ofono_gprs_context *gc;
DBG("%p", modem);
ofono_netreg_create(modem, OFONO_VENDOR_TELIT, "atmodem", data->chat);
ofono_ussd_create(modem, 0, "atmodem", data->chat);
ofono_call_forwarding_create(modem, 0, "atmodem", data->chat);
ofono_call_settings_create(modem, 0, "atmodem", data->chat);
ofono_call_meter_create(modem, 0, "atmodem", data->chat);
ofono_call_barring_create(modem, 0, "atmodem", data->chat);
mw = ofono_message_waiting_create(modem);
if (mw)
ofono_message_waiting_register(mw);
if (data->has_voice) {
struct ofono_message_waiting *mw;
ofono_voicecall_create(modem, 0, "atmodem", data->chat);
ofono_ussd_create(modem, 0, "atmodem", data->chat);
ofono_call_forwarding_create(modem, 0, "atmodem", data->chat);
ofono_call_settings_create(modem, 0, "atmodem", data->chat);
ofono_call_meter_create(modem, 0, "atmodem", data->chat);
ofono_call_barring_create(modem, 0, "atmodem", data->chat);
mw = ofono_message_waiting_create(modem);
if (mw)
ofono_message_waiting_register(mw);
}
gprs = ofono_gprs_create(modem, OFONO_VENDOR_TELIT, "atmodem",
data->chat);
if (data->model == LE910)
gc = ofono_gprs_context_create(modem, OFONO_VENDOR_TELIT,
"telitncmmodem", data->modem);
else
gc = ofono_gprs_context_create(modem, 0, "atmodem",
data->modem);
if (gprs && gc)
ofono_gprs_add_context(gprs, gc);
}
static struct bluetooth_sap_driver sap_driver = {
.name = "telit",
.enable = telit_sap_enable,
.pre_sim = telit_pre_sim,
.post_sim = telit_post_sim,
.set_online = telit_set_online,
.post_online = telit_post_online,
.disable = telit_sap_disable,
};
static int telit_probe(struct ofono_modem *modem)
{
struct telit_data *data;
@@ -644,8 +518,6 @@ static int telit_probe(struct ofono_modem *modem)
ofono_modem_set_data(modem, data);
bluetooth_sap_client_register(&sap_driver, modem);
return 0;
}
@@ -655,8 +527,6 @@ static void telit_remove(struct ofono_modem *modem)
DBG("%p", modem);
bluetooth_sap_client_unregister(modem);
ofono_modem_set_data(modem, NULL);
/* Cleanup after hot-unplug */
@@ -672,14 +542,14 @@ static struct ofono_modem_driver telit_driver = {
.remove = telit_remove,
.enable = telit_enable,
.disable = telit_disable,
.set_online = telit_set_online,
.pre_sim = telit_pre_sim,
.post_sim = telit_post_sim,
.post_online = telit_post_online,
};
static int telit_init(void)
{
DBG("");
return ofono_modem_driver_register(&telit_driver);
}
@@ -688,5 +558,5 @@ static void telit_exit(void)
ofono_modem_driver_unregister(&telit_driver);
}
OFONO_PLUGIN_DEFINE(telit, "telit driver", VERSION,
OFONO_PLUGIN_DEFINE(telit, "Telit driver", VERSION,
OFONO_PLUGIN_PRIORITY_DEFAULT, telit_init, telit_exit)

View File

@@ -38,6 +38,8 @@
#include <ofono/sim.h>
#include <ofono/gprs.h>
#include <ofono/gprs-context.h>
#include <ofono/netmon.h>
#include <ofono/lte.h>
#include <drivers/atmodem/atutil.h>
#include <drivers/atmodem/vendor.h>
@@ -313,6 +315,8 @@ static void ublox_post_sim(struct ofono_modem *modem)
--ncontexts;
}
ofono_lte_create(modem, "ubloxmodem", data->aux);
}
static void ublox_post_online(struct ofono_modem *modem)
@@ -320,6 +324,8 @@ static void ublox_post_online(struct ofono_modem *modem)
struct ublox_data *data = ofono_modem_get_data(modem);
ofono_netreg_create(modem, data->vendor_family, "atmodem", data->aux);
ofono_netmon_create(modem, data->vendor_family, "ubloxmodem", data->aux);
}
static struct ofono_modem_driver ublox_driver = {

View File

@@ -1,545 +0,0 @@
/*
*
* oFono - Open Source Telephony
*
* Copyright (C) 2008-2011 Intel Corporation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <errno.h>
#include <ctype.h>
#include <stdlib.h>
#include <libudev.h>
#include <glib.h>
#include <string.h>
#define OFONO_API_SUBJECT_TO_CHANGE
#include <ofono/plugin.h>
#include <ofono/modem.h>
#include <ofono/log.h>
static GSList *modem_list = NULL;
static GHashTable *devpath_list = NULL;
static struct ofono_modem *find_modem(const char *devpath)
{
GSList *list;
for (list = modem_list; list; list = list->next) {
struct ofono_modem *modem = list->data;
const char *path = ofono_modem_get_string(modem, "Path");
if (g_strcmp0(devpath, path) == 0)
return modem;
}
return NULL;
}
static const char *get_property(struct udev_device *device,
char const *property_name)
{
struct udev_list_entry *entry;
entry = udev_device_get_properties_list_entry(device);
while (entry) {
const char *name = udev_list_entry_get_name(entry);
if (g_strcmp0(name, property_name) == 0)
return udev_list_entry_get_value(entry);
entry = udev_list_entry_get_next(entry);
}
return NULL;
}
static const char *get_driver(struct udev_device *udev_device)
{
return get_property(udev_device, "OFONO_DRIVER");
}
static const char *get_serial(struct udev_device *udev_device)
{
const char *serial;
serial = get_property(udev_device, "ID_SERIAL_SHORT");
if (serial != NULL) {
unsigned int i, len = strlen(serial);
for (i = 0; i < len; i++) {
if (!g_ascii_isalnum(serial[i]))
return NULL;
}
}
return serial;
}
static void add_ifx(struct ofono_modem *modem,
struct udev_device *udev_device)
{
struct udev_list_entry *entry;
const char *devnode;
DBG("modem %p", modem);
devnode = udev_device_get_devnode(udev_device);
ofono_modem_set_string(modem, "Device", devnode);
entry = udev_device_get_properties_list_entry(udev_device);
while (entry) {
const char *name = udev_list_entry_get_name(entry);
const char *value = udev_list_entry_get_value(entry);
if (g_str_equal(name, "OFONO_IFX_LDISC") == TRUE)
ofono_modem_set_string(modem, "LineDiscipline", value);
else if (g_str_equal(name, "OFONO_IFX_AUDIO") == TRUE)
ofono_modem_set_string(modem, "AudioSetting", value);
else if (g_str_equal(name, "OFONO_IFX_LOOPBACK") == TRUE)
ofono_modem_set_string(modem, "AudioLoopback", value);
entry = udev_list_entry_get_next(entry);
}
ofono_modem_register(modem);
}
static void add_isi(struct ofono_modem *modem,
struct udev_device *udev_device)
{
const char *ifname, *type, *addr;
DBG("modem %p", modem);
if (ofono_modem_get_string(modem, "Interface"))
return;
addr = get_property(udev_device, "OFONO_ISI_ADDRESS");
if (addr != NULL)
ofono_modem_set_integer(modem, "Address", atoi(addr));
if (g_strcmp0(udev_device_get_subsystem(udev_device), "net") != 0)
return;
type = udev_device_get_sysattr_value(udev_device, "type");
if (g_strcmp0(type, "820") != 0)
return;
ifname = udev_device_get_sysname(udev_device);
ofono_modem_set_string(modem, "Interface", ifname);
DBG("interface %s", ifname);
ofono_modem_register(modem);
}
static void add_calypso(struct ofono_modem *modem,
struct udev_device *udev_device)
{
const char *devnode;
DBG("modem %p", modem);
devnode = udev_device_get_devnode(udev_device);
ofono_modem_set_string(modem, "Device", devnode);
ofono_modem_register(modem);
}
static void add_wavecom(struct ofono_modem *modem,
struct udev_device *udev_device)
{
const char *devnode;
struct udev_list_entry *entry;
DBG("modem %p", modem);
devnode = udev_device_get_devnode(udev_device);
ofono_modem_set_string(modem, "Device", devnode);
entry = udev_device_get_properties_list_entry(udev_device);
while (entry) {
const char *name = udev_list_entry_get_name(entry);
const char *value = udev_list_entry_get_value(entry);
if (g_str_equal(name, "OFONO_WAVECOM_MODEL") == TRUE)
ofono_modem_set_string(modem, "Model", value);
entry = udev_list_entry_get_next(entry);
}
ofono_modem_register(modem);
}
static void add_cinterion(struct ofono_modem *modem,
struct udev_device *udev_device)
{
const char *devnode;
DBG("modem %p", modem);
devnode = udev_device_get_devnode(udev_device);
ofono_modem_set_string(modem, "Device", devnode);
ofono_modem_register(modem);
}
static void add_nokiacdma(struct ofono_modem *modem,
struct udev_device *udev_device)
{
const char *devnode;
DBG("modem %p", modem);
devnode = udev_device_get_devnode(udev_device);
ofono_modem_set_string(modem, "Device", devnode);
ofono_modem_register(modem);
}
static void add_sim900(struct ofono_modem *modem,
struct udev_device *udev_device)
{
const char *devnode;
DBG("modem %p", modem);
devnode = udev_device_get_devnode(udev_device);
ofono_modem_set_string(modem, "Device", devnode);
ofono_modem_register(modem);
}
static void add_modem(struct udev_device *udev_device)
{
struct ofono_modem *modem;
struct udev_device *parent;
const char *devpath, *curpath, *driver;
driver = get_driver(udev_device);
if (driver != NULL) {
devpath = udev_device_get_devpath(udev_device);
if (devpath == NULL)
return;
if(g_strcmp0(driver, "tc65") == 0)
driver = "cinterion";
if(g_strcmp0(driver, "ehs6") == 0)
driver = "cinterion";
modem = ofono_modem_create(NULL, driver);
if (modem == NULL)
return;
ofono_modem_set_string(modem, "Path", devpath);
modem_list = g_slist_prepend(modem_list, modem);
goto done;
}
parent = udev_device_get_parent(udev_device);
if (parent == NULL)
return;
driver = get_driver(parent);
if (driver == NULL) {
parent = udev_device_get_parent(parent);
driver = get_driver(parent);
if (driver == NULL) {
parent = udev_device_get_parent(parent);
driver = get_driver(parent);
if (driver == NULL)
return;
}
}
devpath = udev_device_get_devpath(parent);
if (devpath == NULL)
return;
modem = find_modem(devpath);
if (modem == NULL) {
const char *serial = get_serial(parent);
modem = ofono_modem_create(serial, driver);
if (modem == NULL)
return;
ofono_modem_set_string(modem, "Path", devpath);
ofono_modem_set_integer(modem, "Registered", 0);
modem_list = g_slist_prepend(modem_list, modem);
}
done:
curpath = udev_device_get_devpath(udev_device);
if (curpath == NULL)
return;
DBG("%s (%s)", curpath, driver);
g_hash_table_insert(devpath_list, g_strdup(curpath), g_strdup(devpath));
if (g_strcmp0(driver, "ifx") == 0)
add_ifx(modem, udev_device);
else if (g_strcmp0(driver, "u8500") == 0)
add_isi(modem, udev_device);
else if (g_strcmp0(driver, "n900") == 0)
add_isi(modem, udev_device);
else if (g_strcmp0(driver, "calypso") == 0)
add_calypso(modem, udev_device);
else if (g_strcmp0(driver, "cinterion") == 0)
add_cinterion(modem, udev_device);
else if (g_strcmp0(driver, "nokiacdma") == 0)
add_nokiacdma(modem, udev_device);
else if (g_strcmp0(driver, "sim900") == 0)
add_sim900(modem, udev_device);
else if (g_strcmp0(driver, "wavecom") == 0)
add_wavecom(modem, udev_device);
}
static gboolean devpath_remove(gpointer key, gpointer value, gpointer user_data)
{
const char *path = value;
const char *devpath = user_data;
DBG("%s -> %s", path, devpath);
return g_str_equal(path, devpath);
}
static void remove_modem(struct udev_device *udev_device)
{
struct ofono_modem *modem;
const char *curpath = udev_device_get_devpath(udev_device);
char *devpath, *remove;
if (curpath == NULL)
return;
DBG("%s", curpath);
devpath = g_hash_table_lookup(devpath_list, curpath);
if (devpath == NULL)
return;
modem = find_modem(devpath);
if (modem == NULL)
return;
modem_list = g_slist_remove(modem_list, modem);
ofono_modem_remove(modem);
DBG("%s", devpath);
remove = g_strdup(devpath);
g_hash_table_foreach_remove(devpath_list, devpath_remove, remove);
g_free(remove);
}
static void enumerate_devices(struct udev *context)
{
struct udev_enumerate *enumerate;
struct udev_list_entry *entry;
enumerate = udev_enumerate_new(context);
if (enumerate == NULL)
return;
udev_enumerate_add_match_subsystem(enumerate, "tty");
udev_enumerate_add_match_subsystem(enumerate, "net");
udev_enumerate_add_match_subsystem(enumerate, "hsi");
udev_enumerate_scan_devices(enumerate);
entry = udev_enumerate_get_list_entry(enumerate);
while (entry) {
const char *syspath = udev_list_entry_get_name(entry);
struct udev_device *device;
device = udev_device_new_from_syspath(context, syspath);
if (device != NULL) {
const char *subsystem;
subsystem = udev_device_get_subsystem(device);
if (g_strcmp0(subsystem, "tty") == 0 ||
g_strcmp0(subsystem, "net") == 0 ||
g_strcmp0(subsystem, "hsi") == 0)
add_modem(device);
udev_device_unref(device);
}
entry = udev_list_entry_get_next(entry);
}
udev_enumerate_unref(enumerate);
}
static struct udev *udev_ctx;
static struct udev_monitor *udev_mon;
static guint udev_watch = 0;
static gboolean udev_event(GIOChannel *channel, GIOCondition cond,
gpointer user_data)
{
struct udev_device *device;
const char *subsystem, *action;
if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
ofono_warn("Error with udev monitor channel");
udev_watch = 0;
return FALSE;
}
device = udev_monitor_receive_device(udev_mon);
if (device == NULL)
return TRUE;
subsystem = udev_device_get_subsystem(device);
if (subsystem == NULL)
goto done;
action = udev_device_get_action(device);
if (action == NULL)
goto done;
DBG("subsystem %s %s", subsystem, action);
if (g_str_equal(action, "add") == TRUE) {
if (g_strcmp0(subsystem, "tty") == 0 ||
g_strcmp0(subsystem, "net") == 0 ||
g_strcmp0(subsystem, "hsi") == 0)
add_modem(device);
} else if (g_str_equal(action, "remove") == TRUE) {
if (g_strcmp0(subsystem, "tty") == 0 ||
g_strcmp0(subsystem, "net") == 0 ||
g_strcmp0(subsystem, "hsi") == 0)
remove_modem(device);
}
DBG("subsystem %s finished", subsystem);
done:
udev_device_unref(device);
return TRUE;
}
static void udev_start(void)
{
GIOChannel *channel;
int fd;
if (udev_monitor_enable_receiving(udev_mon) < 0) {
ofono_error("Failed to enable udev monitor");
return;
}
enumerate_devices(udev_ctx);
fd = udev_monitor_get_fd(udev_mon);
channel = g_io_channel_unix_new(fd);
if (channel == NULL)
return;
udev_watch = g_io_add_watch(channel,
G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
udev_event, NULL);
g_io_channel_unref(channel);
}
static int udev_init(void)
{
devpath_list = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, g_free);
if (devpath_list == NULL) {
ofono_error("Failed to create udev path list");
return -ENOMEM;
}
udev_ctx = udev_new();
if (udev_ctx == NULL) {
ofono_error("Failed to create udev context");
g_hash_table_destroy(devpath_list);
return -EIO;
}
udev_mon = udev_monitor_new_from_netlink(udev_ctx, "udev");
if (udev_mon == NULL) {
ofono_error("Failed to create udev monitor");
g_hash_table_destroy(devpath_list);
udev_unref(udev_ctx);
udev_ctx = NULL;
return -EIO;
}
udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "tty", NULL);
udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "net", NULL);
udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "hsi", NULL);
udev_monitor_filter_update(udev_mon);
udev_start();
return 0;
}
static void udev_exit(void)
{
GSList *list;
if (udev_watch > 0)
g_source_remove(udev_watch);
for (list = modem_list; list; list = list->next) {
struct ofono_modem *modem = list->data;
ofono_modem_remove(modem);
}
g_slist_free(modem_list);
modem_list = NULL;
g_hash_table_destroy(devpath_list);
devpath_list = NULL;
if (udev_ctx == NULL)
return;
udev_monitor_filter_remove(udev_mon);
udev_monitor_unref(udev_mon);
udev_unref(udev_ctx);
}
OFONO_PLUGIN_DEFINE(udev, "udev hardware detection", VERSION,
OFONO_PLUGIN_PRIORITY_DEFAULT, udev_init, udev_exit)

View File

@@ -37,13 +37,22 @@
#include <ofono/modem.h>
#include <ofono/log.h>
enum modem_type {
MODEM_TYPE_USB,
MODEM_TYPE_SERIAL,
};
struct modem_info {
char *syspath;
char *devname;
char *driver;
char *vendor;
char *model;
GSList *devices;
enum modem_type type;
union {
GSList *devices;
struct serial_device_info* serial;
};
struct ofono_modem *modem;
const char *sysattr;
};
@@ -58,6 +67,13 @@ struct device_info {
char *subsystem;
};
struct serial_device_info {
char *devpath;
char *devnode;
char *subsystem;
struct udev_device* dev;
};
static gboolean setup_isi(struct modem_info *modem)
{
const char *node = NULL;
@@ -188,20 +204,31 @@ static gboolean setup_gobi(struct modem_info *modem)
for (list = modem->devices; list; list = list->next) {
struct device_info *info = list->data;
DBG("%s %s %s %s", info->devnode, info->interface,
info->number, info->label);
DBG("%s %s %s %s %s %s", info->devnode, info->interface,
info->number, info->label,
info->sysattr, info->subsystem);
if (g_strcmp0(info->interface, "255/255/255") == 0) {
if (info->number == NULL)
qmi = info->devnode;
else if (g_strcmp0(info->number, "00") == 0)
net = info->devnode;
else if (g_strcmp0(info->number, "01") == 0)
diag = info->devnode;
else if (g_strcmp0(info->number, "02") == 0)
mdm = info->devnode;
else if (g_strcmp0(info->number, "03") == 0)
gps = info->devnode;
if (g_strcmp0(info->subsystem, "usbmisc") == 0) /* cdc-wdm */
qmi = info->devnode;
else if (g_strcmp0(info->subsystem, "net") == 0) /* wwan */
net = info->devnode;
else if (g_strcmp0(info->subsystem, "tty") == 0) {
if (g_strcmp0(info->interface, "255/255/255") == 0) {
if (g_strcmp0(info->number, "00") == 0)
diag = info->devnode; /* ec20 */
else if (g_strcmp0(info->number, "01") == 0)
diag = info->devnode; /* gobi */
else if (g_strcmp0(info->number, "02") == 0)
mdm = info->devnode; /* gobi */
else if (g_strcmp0(info->number, "03") == 0)
gps = info->devnode; /* gobi */
} else if (g_strcmp0(info->interface, "255/0/0") == 0) {
if (g_strcmp0(info->number, "01") == 0)
gps = info->devnode; /* ec20 */
if (g_strcmp0(info->number, "02") == 0)
mdm = info->devnode; /* ec20 */
/* ignore the 3rd device second AT/mdm iface */
}
}
}
@@ -252,8 +279,6 @@ static gboolean setup_sierra(struct modem_info *modem)
if (qmi != NULL && net != NULL) {
ofono_modem_set_driver(modem->modem, "gobi");
/* Fixup SIM interface for Sierra QMI devices */
ofono_modem_set_boolean(modem->modem, "ForceSimLegacy", TRUE);
goto done;
}
@@ -272,42 +297,6 @@ done:
return TRUE;
}
static gboolean setup_option(struct modem_info *modem)
{
const char *aux = NULL, *mdm = NULL, *diag = NULL;
GSList *list;
DBG("%s", modem->syspath);
for (list = modem->devices; list; list = list->next) {
struct device_info *info = list->data;
DBG("%s %s %s %s", info->devnode, info->interface,
info->number, info->label);
if (g_strcmp0(info->interface, "255/255/255") == 0) {
if (g_strcmp0(info->number, "00") == 0)
mdm = info->devnode;
else if (g_strcmp0(info->number, "01") == 0)
diag = info->devnode;
else if (g_strcmp0(info->number, "02") == 0)
aux = info->devnode;
}
}
if (aux == NULL || mdm == NULL)
return FALSE;
DBG("aux=%s modem=%s diag=%s", aux, mdm, diag);
ofono_modem_set_string(modem->modem, "Aux", aux);
ofono_modem_set_string(modem->modem, "Modem", mdm);
ofono_modem_set_string(modem->modem, "Diag", diag);
return TRUE;
}
static gboolean setup_huawei(struct modem_info *modem)
{
const char *qmi = NULL, *mdm = NULL, *net = NULL;
@@ -613,7 +602,7 @@ static gboolean setup_nokia(struct modem_info *modem)
static gboolean setup_telit(struct modem_info *modem)
{
const char *mdm = NULL, *aux = NULL, *gps = NULL, *diag = NULL;
const char *mdm = NULL, *aux = NULL, *gps = NULL, *net = NULL;
GSList *list;
DBG("%s", modem->syspath);
@@ -635,30 +624,41 @@ static gboolean setup_telit(struct modem_info *modem)
} else if (g_strcmp0(info->interface, "255/255/255") == 0) {
if (g_strcmp0(info->number, "00") == 0)
mdm = info->devnode;
else if (g_strcmp0(info->number, "01") == 0)
diag = info->devnode;
else if (g_strcmp0(info->number, "02") == 0)
gps = info->devnode;
else if (g_strcmp0(info->number, "03") == 0)
aux = info->devnode;
} else if (g_strcmp0(info->interface, "2/2/1") == 0) {
if (g_strcmp0(info->number, "00") == 0)
mdm = info->devnode;
else if (g_strcmp0(info->number, "06") == 0)
aux = info->devnode;
else if (g_strcmp0(info->number, "0a") == 0)
gps = info->devnode;
} else if (info->sysattr && (g_str_has_suffix(info->sysattr,
"CDC NCM") == TRUE)) {
net = info->devnode;
}
}
if (aux == NULL || mdm == NULL)
return FALSE;
DBG("modem=%s aux=%s gps=%s diag=%s", mdm, aux, gps, diag);
DBG("modem=%s aux=%s gps=%s net=%s", mdm, aux, gps, net);
ofono_modem_set_string(modem->modem, "Modem", mdm);
ofono_modem_set_string(modem->modem, "Aux", aux);
ofono_modem_set_string(modem->modem, "GPS", gps);
if (net != NULL)
ofono_modem_set_string(modem->modem, "NetworkInterface", net);
return TRUE;
}
static gboolean setup_he910(struct modem_info *modem)
static gboolean setup_telitqmi(struct modem_info *modem)
{
const char *mdm = NULL, *aux = NULL, *gps = NULL;
const char *qmi = NULL, *net = NULL;
GSList *list;
DBG("%s", modem->syspath);
@@ -666,27 +666,29 @@ static gboolean setup_he910(struct modem_info *modem)
for (list = modem->devices; list; list = list->next) {
struct device_info *info = list->data;
DBG("%s %s %s %s", info->devnode, info->interface,
info->number, info->label);
DBG("%s %s %s %s %s", info->devnode, info->interface,
info->number, info->label, info->subsystem);
if (g_strcmp0(info->interface, "2/2/1") == 0) {
if (g_strcmp0(info->number, "00") == 0)
mdm = info->devnode;
else if (g_strcmp0(info->number, "06") == 0)
aux = info->devnode;
else if (g_strcmp0(info->number, "0a") == 0)
gps = info->devnode;
if (g_strcmp0(info->interface, "255/255/255") == 0 &&
g_strcmp0(info->number, "02") == 0) {
if (g_strcmp0(info->subsystem, "net") == 0)
net = info->devnode;
else if (g_strcmp0(info->subsystem, "usbmisc") == 0)
qmi = info->devnode;
}
}
if (aux == NULL || mdm == NULL)
if (qmi == NULL || net == NULL)
return FALSE;
DBG("modem=%s aux=%s gps=%s", mdm, aux, gps);
DBG("qmi=%s net=%s", qmi, net);
ofono_modem_set_string(modem->modem, "Modem", mdm);
ofono_modem_set_string(modem->modem, "Aux", aux);
ofono_modem_set_string(modem->modem, "GPS", gps);
ofono_modem_set_string(modem->modem, "Device", qmi);
ofono_modem_set_string(modem->modem, "NetworkInterface", net);
ofono_modem_set_boolean(modem->modem, "ForceSimLegacy", TRUE);
ofono_modem_set_boolean(modem->modem, "AlwaysOnline", TRUE);
ofono_modem_set_driver(modem->modem, "gobi");
return TRUE;
}
@@ -856,6 +858,143 @@ static gboolean setup_quectel(struct modem_info *modem)
return TRUE;
}
static gboolean setup_quectelqmi(struct modem_info *modem)
{
const char *qmi = NULL, *net = NULL, *gps = NULL;
GSList *list;
DBG("%s", modem->syspath);
for (list = modem->devices; list; list = g_slist_next(list)) {
struct device_info *info = list->data;
DBG("%s %s %s %s %s", info->devnode, info->interface,
info->number, info->label, info->subsystem);
if (g_strcmp0(info->interface, "255/255/255") == 0 &&
g_strcmp0(info->number, "04") == 0) {
if (g_strcmp0(info->subsystem, "net") == 0)
net = info->devnode;
else if (g_strcmp0(info->subsystem, "usbmisc") == 0)
qmi = info->devnode;
} else if (g_strcmp0(info->interface, "255/0/0") == 0 &&
g_strcmp0(info->number, "02") == 0) {
gps = info->devnode;
}
}
DBG("qmi=%s net=%s", qmi, net);
if (qmi == NULL || net == NULL)
return FALSE;
DBG("qmi=%s net=%s", qmi, net);
ofono_modem_set_string(modem->modem, "Device", qmi);
ofono_modem_set_string(modem->modem, "NetworkInterface", net);
if (gps)
ofono_modem_set_string(modem->modem, "GPS", gps);
ofono_modem_set_driver(modem->modem, "gobi");
return TRUE;
}
static gboolean setup_serial_modem(struct modem_info* modem)
{
struct serial_device_info* info;
info = modem->serial;
ofono_modem_set_string(modem->modem, "Device", info->devnode);
return TRUE;
}
static gboolean setup_tc65(struct modem_info* modem)
{
ofono_modem_set_driver(modem->modem, "cinterion");
return setup_serial_modem(modem);
}
static gboolean setup_ehs6(struct modem_info* modem)
{
ofono_modem_set_driver(modem->modem, "cinterion");
return setup_serial_modem(modem);
}
static gboolean setup_ifx(struct modem_info* modem)
{
struct serial_device_info* info;
const char *value;
info = modem->serial;
value = udev_device_get_property_value(info->dev, "OFONO_IFX_LDISC");
if (value)
ofono_modem_set_string(modem->modem, "LineDiscipline", value);
value = udev_device_get_property_value(info->dev, "OFONO_IFX_AUDIO");
if (value)
ofono_modem_set_string(modem->modem, "AudioSetting", value);
value = udev_device_get_property_value(info->dev, "OFONO_IFX_LOOPBACK");
if (value)
ofono_modem_set_string(modem->modem, "AudioLoopback", value);
ofono_modem_set_string(modem->modem, "Device", info->devnode);
return TRUE;
}
static gboolean setup_wavecom(struct modem_info* modem)
{
struct serial_device_info* info;
const char *value;
info = modem->serial;
value = udev_device_get_property_value(info->dev,
"OFONO_WAVECOM_MODEL");
if (value)
ofono_modem_set_string(modem->modem, "Model", value);
ofono_modem_set_string(modem->modem, "Device", info->devnode);
return TRUE;
}
static gboolean setup_isi_serial(struct modem_info* modem)
{
struct serial_device_info* info;
const char *value;
info = modem->serial;
if (g_strcmp0(udev_device_get_subsystem(info->dev), "net") != 0)
return FALSE;
value = udev_device_get_sysattr_value(info->dev, "type");
if (g_strcmp0(value, "820") != 0)
return FALSE;
/* OK, we want this device to be a modem */
value = udev_device_get_sysname(info->dev);
if (value)
ofono_modem_set_string(modem->modem, "Interface", value);
value = udev_device_get_property_value(info->dev, "OFONO_ISI_ADDRESS");
if (value)
ofono_modem_set_integer(modem->modem, "Address", atoi(value));
ofono_modem_set_string(modem->modem, "Device", info->devnode);
return TRUE;
}
static gboolean setup_ublox(struct modem_info *modem)
{
const char *aux = NULL, *mdm = NULL, *net = NULL;
@@ -915,6 +1054,50 @@ static gboolean setup_ublox(struct modem_info *modem)
return TRUE;
}
static gboolean setup_gemalto(struct modem_info* modem)
{
const char *app = NULL, *gps = NULL, *mdm = NULL,
*net = NULL, *qmi = NULL;
GSList *list;
DBG("%s", modem->syspath);
for (list = modem->devices; list; list = list->next) {
struct device_info *info = list->data;
DBG("%s %s %s %s %s", info->devnode, info->interface,
info->number, info->label, info->subsystem);
if (g_strcmp0(info->interface, "255/255/255") == 0) {
if (g_strcmp0(info->number, "01") == 0)
gps = info->devnode;
else if (g_strcmp0(info->number, "02") == 0)
app = info->devnode;
else if (g_strcmp0(info->number, "03") == 0)
mdm = info->devnode;
else if (g_strcmp0(info->subsystem, "net") == 0)
net = info->devnode;
else if (g_strcmp0(info->subsystem, "usbmisc") == 0)
qmi = info->devnode;
}
}
DBG("application=%s gps=%s modem=%s network=%s qmi=%s",
app, gps, mdm, net, qmi);
if (app == NULL || mdm == NULL)
return FALSE;
ofono_modem_set_string(modem->modem, "Application", app);
ofono_modem_set_string(modem->modem, "GPS", gps);
ofono_modem_set_string(modem->modem, "Modem", mdm);
ofono_modem_set_string(modem->modem, "Device", qmi);
ofono_modem_set_string(modem->modem, "NetworkInterface", net);
return TRUE;
}
static struct {
const char *name;
gboolean (*setup)(struct modem_info *modem);
@@ -925,7 +1108,6 @@ static struct {
{ "hso", setup_hso, "hsotype" },
{ "gobi", setup_gobi },
{ "sierra", setup_sierra },
{ "option", setup_option },
{ "huawei", setup_huawei },
{ "speedupcdma",setup_speedup },
{ "speedup", setup_speedup },
@@ -933,14 +1115,27 @@ static struct {
{ "alcatel", setup_alcatel },
{ "novatel", setup_novatel },
{ "nokia", setup_nokia },
{ "telit", setup_telit },
{ "he910", setup_he910 },
{ "telit", setup_telit, "device/interface" },
{ "telitqmi", setup_telitqmi },
{ "simcom", setup_simcom },
{ "zte", setup_zte },
{ "icera", setup_icera },
{ "samsung", setup_samsung },
{ "quectel", setup_quectel },
{ "quectelqmi", setup_quectelqmi},
{ "ublox", setup_ublox },
{ "gemalto", setup_gemalto },
/* Following are non-USB modems */
{ "ifx", setup_ifx },
{ "u8500", setup_isi_serial },
{ "n900", setup_isi_serial },
{ "calypso", setup_serial_modem },
{ "cinterion", setup_serial_modem },
{ "nokiacdma", setup_serial_modem },
{ "sim900", setup_serial_modem },
{ "wavecom", setup_wavecom },
{ "tc65", setup_tc65 },
{ "ehs6", setup_ehs6 },
{ }
};
@@ -958,6 +1153,27 @@ static const char *get_sysattr(const char *driver)
return NULL;
}
static void device_info_free(struct device_info* info)
{
g_free(info->devpath);
g_free(info->devnode);
g_free(info->interface);
g_free(info->number);
g_free(info->label);
g_free(info->sysattr);
g_free(info->subsystem);
g_free(info);
}
static void serial_device_info_free(struct serial_device_info* info)
{
g_free(info->devpath);
g_free(info->devnode);
g_free(info->subsystem);
udev_device_unref(info->dev);
g_free(info);
}
static void destroy_modem(gpointer data)
{
struct modem_info *modem = data;
@@ -967,25 +1183,22 @@ static void destroy_modem(gpointer data)
ofono_modem_remove(modem->modem);
for (list = modem->devices; list; list = list->next) {
struct device_info *info = list->data;
switch (modem->type) {
case MODEM_TYPE_USB:
for (list = modem->devices; list; list = list->next) {
struct device_info *info = list->data;
DBG("%s", info->devnode);
DBG("%s", info->devnode);
device_info_free(info);
}
g_free(info->devpath);
g_free(info->devnode);
g_free(info->interface);
g_free(info->number);
g_free(info->label);
g_free(info->sysattr);
g_free(info->subsystem);
g_free(info);
list->data = NULL;
g_slist_free(modem->devices);
break;
case MODEM_TYPE_SERIAL:
serial_device_info_free(modem->serial);
break;
}
g_slist_free(modem->devices);
g_free(modem->syspath);
g_free(modem->devname);
g_free(modem->driver);
@@ -1032,11 +1245,103 @@ static gint compare_device(gconstpointer a, gconstpointer b)
return g_strcmp0(info1->number, info2->number);
}
/*
* Here we try to find the "modem device".
*
* In this variant we identify the "modem device" as simply the device
* that has the OFONO_DRIVER property. If the device node doesn't
* have this property itself, then we do a brute force search for it
* through the device hierarchy.
*
*/
static struct udev_device* get_serial_modem_device(struct udev_device *dev)
{
const char* driver;
while (dev) {
driver = udev_device_get_property_value(dev, "OFONO_DRIVER");
if (driver)
return dev;
dev = udev_device_get_parent(dev);
}
return NULL;
}
/*
* Add 'legacy' device
*
* The term legacy is a bit misleading, but this adds devices according
* to the original ofono model.
*
* - We cannot assume that these are USB devices
* - The modem consists of only a single interface
* - The device must have an OFONO_DRIVER property from udev
*/
static void add_serial_device(struct udev_device *dev)
{
const char *syspath, *devpath, *devname, *devnode;
struct modem_info *modem;
struct serial_device_info *info;
const char *subsystem;
struct udev_device* mdev;
const char* driver;
mdev = get_serial_modem_device(dev);
if (!mdev) {
DBG("Device is missing required OFONO_DRIVER property");
return;
}
driver = udev_device_get_property_value(mdev, "OFONO_DRIVER");
syspath = udev_device_get_syspath(mdev);
devname = udev_device_get_devnode(mdev);
devpath = udev_device_get_devpath(mdev);
devnode = udev_device_get_devnode(dev);
if (!syspath || !devname || !devpath || !devnode)
return;
modem = g_hash_table_lookup(modem_list, syspath);
if (modem == NULL) {
modem = g_try_new0(struct modem_info, 1);
if (modem == NULL)
return;
modem->type = MODEM_TYPE_SERIAL;
modem->syspath = g_strdup(syspath);
modem->devname = g_strdup(devname);
modem->driver = g_strdup("legacy");
g_hash_table_replace(modem_list, modem->syspath, modem);
}
subsystem = udev_device_get_subsystem(dev);
DBG("%s", syspath);
DBG("%s", devpath);
DBG("%s (%s)", devnode, driver);
info = g_try_new0(struct serial_device_info, 1);
if (info == NULL)
return;
info->devpath = g_strdup(devpath);
info->devnode = g_strdup(devnode);
info->subsystem = g_strdup(subsystem);
info->dev = udev_device_ref(dev);
modem->devices = g_slist_append(modem->devices, info);
}
static void add_device(const char *syspath, const char *devname,
const char *driver, const char *vendor,
const char *model, struct udev_device *device)
{
struct udev_device *intf;
struct udev_device *usb_interface;
const char *devpath, *devnode, *interface, *number;
const char *label, *sysattr, *subsystem;
struct modem_info *modem;
@@ -1054,9 +1359,9 @@ static void add_device(const char *syspath, const char *devname,
return;
}
intf = udev_device_get_parent_with_subsystem_devtype(device,
usb_interface = udev_device_get_parent_with_subsystem_devtype(device,
"usb", "usb_interface");
if (intf == NULL)
if (usb_interface == NULL)
return;
modem = g_hash_table_lookup(modem_list, syspath);
@@ -1065,6 +1370,7 @@ static void add_device(const char *syspath, const char *devname,
if (modem == NULL)
return;
modem->type = MODEM_TYPE_USB;
modem->syspath = g_strdup(syspath);
modem->devname = g_strdup(devname);
modem->driver = g_strdup(driver);
@@ -1076,7 +1382,7 @@ static void add_device(const char *syspath, const char *devname,
g_hash_table_replace(modem_list, modem->syspath, modem);
}
interface = udev_device_get_property_value(intf, "INTERFACE");
interface = udev_device_get_property_value(usb_interface, "INTERFACE");
number = udev_device_get_property_value(device, "ID_USB_INTERFACE_NUM");
/* If environment variable is not set, get value from attributes (or parent's ones) */
@@ -1092,6 +1398,10 @@ static void add_device(const char *syspath, const char *devname,
}
label = udev_device_get_property_value(device, "OFONO_LABEL");
if (!label)
label = udev_device_get_property_value(usb_interface,
"OFONO_LABEL");
subsystem = udev_device_get_subsystem(device);
if (modem->sysattr != NULL)
@@ -1171,15 +1481,26 @@ static struct {
{ "simcom", "option", "05c6", "9000" },
{ "telit", "usbserial", "1bc7" },
{ "telit", "option", "1bc7" },
{ "he910", "cdc_acm", "1bc7", "0021" },
{ "telit", "cdc_acm", "1bc7", "0021" },
{ "telitqmi", "qmi_wwan", "1bc7", "1201" },
{ "telitqmi", "option", "1bc7", "1201" },
{ "nokia", "option", "0421", "060e" },
{ "nokia", "option", "0421", "0623" },
{ "samsung", "option", "04e8", "6889" },
{ "samsung", "kalmia" },
{ "quectel", "option", "05c6", "9090" },
{ "quectelqmi", "qmi_wwan", "2c7c", "0121" },
{ "quectelqmi", "qcserial", "2c7c", "0121" },
{ "quectelqmi", "qmi_wwan", "2c7c", "0125" },
{ "quectelqmi", "qcserial", "2c7c", "0125" },
{ "ublox", "cdc_acm", "1546", "1102" },
{ "ublox", "rndis_host", "1546", "1146" },
{ "ublox", "cdc_acm", "1546", "1146" },
{ "gemalto", "option", "1e2d", "0053" },
{ "gemalto", "cdc_wdm", "1e2d", "0053" },
{ "gemalto", "qmi_wwan", "1e2d", "0053" },
{ "telit", "cdc_ncm", "1bc7", "0036" },
{ "telit", "cdc_acm", "1bc7", "0036" },
{ }
};
@@ -1202,9 +1523,22 @@ static void check_usb_device(struct udev_device *device)
if (devname == NULL)
return;
vendor = udev_device_get_property_value(usb_device, "ID_VENDOR_ID");
model = udev_device_get_property_value(usb_device, "ID_MODEL_ID");
driver = udev_device_get_property_value(usb_device, "OFONO_DRIVER");
if (!driver) {
struct udev_device *usb_interface =
udev_device_get_parent_with_subsystem_devtype(
device, "usb", "usb_interface");
if (usb_interface)
driver = udev_device_get_property_value(
usb_interface, "OFONO_DRIVER");
}
if (driver == NULL) {
const char *drv, *vid, *pid;
const char *drv;
unsigned int i;
drv = udev_device_get_property_value(device, "ID_USB_DRIVER");
@@ -1223,40 +1557,24 @@ static void check_usb_device(struct udev_device *device)
}
}
vid = udev_device_get_property_value(device, "ID_VENDOR_ID");
pid = udev_device_get_property_value(device, "ID_MODEL_ID");
DBG("%s [%s:%s]", drv, vid, pid);
DBG("%s [%s:%s]", drv, vendor, model);
for (i = 0; vendor_list[i].driver; i++) {
if (g_str_equal(vendor_list[i].drv, drv) == FALSE)
continue;
if (vendor_list[i].vid == NULL) {
driver = vendor_list[i].driver;
vendor = vid;
model = pid;
continue;
}
if (vid == NULL || pid == NULL)
continue;
if (g_str_equal(vendor_list[i].vid, vid) == TRUE) {
if (vendor_list[i].pid == NULL) {
driver = vendor_list[i].driver;
vendor = vid;
model = pid;
if (vendor_list[i].vid) {
if (!g_str_equal(vendor_list[i].vid, vendor))
continue;
}
if (g_strcmp0(vendor_list[i].pid, pid) == 0) {
driver = vendor_list[i].driver;
vendor = vid;
model = pid;
break;
}
}
if (vendor_list[i].pid) {
if (!g_str_equal(vendor_list[i].pid, model))
continue;
}
driver = vendor_list[i].driver;
}
if (driver == NULL)
@@ -1280,6 +1598,9 @@ static void check_device(struct udev_device *device)
if ((g_str_equal(bus, "usb") == TRUE) ||
(g_str_equal(bus, "usbmisc") == TRUE))
check_usb_device(device);
else
add_serial_device(device);
}
static gboolean create_modem(gpointer key, gpointer value, gpointer user_data)
@@ -1330,6 +1651,7 @@ static void enumerate_devices(struct udev *context)
udev_enumerate_add_match_subsystem(enumerate, "usb");
udev_enumerate_add_match_subsystem(enumerate, "usbmisc");
udev_enumerate_add_match_subsystem(enumerate, "net");
udev_enumerate_add_match_subsystem(enumerate, "hsi");
udev_enumerate_scan_devices(enumerate);
@@ -1454,6 +1776,7 @@ static int detect_init(void)
udev_monitor_filter_add_match_subsystem_devtype(udev_mon,
"usbmisc", NULL);
udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "net", NULL);
udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "hsi", NULL);
udev_monitor_filter_update(udev_mon);

View File

@@ -19,6 +19,10 @@
*
*/
#include <glib.h>
#include <ofono/types.h>
/* 27.007 Section 7.3 <AcT> */
enum access_technology {
ACCESS_TECHNOLOGY_GSM = 0,

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