Compare commits

...

183 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
97 changed files with 5677 additions and 2156 deletions

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

@@ -24,7 +24,7 @@ pkginclude_HEADERS = include/log.h include/plugin.h include/history.h \
include/sim-mnclength.h \
include/handsfree-audio.h include/siri.h \
include/sms-filter.h \
include/netmon.h
include/netmon.h include/lte.h
nodist_pkginclude_HEADERS = include/version.h
@@ -113,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@
@@ -185,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 \
@@ -211,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
@@ -270,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) \
@@ -379,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 \
@@ -441,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
@@ -508,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
@@ -535,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
@@ -548,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
@@ -629,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
@@ -702,9 +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/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
@@ -752,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 \
@@ -859,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
@@ -1072,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)

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;
};
@@ -1459,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\"",
@@ -1490,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\"",
@@ -1550,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",
@@ -1568,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,
@@ -1582,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);
@@ -1591,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

@@ -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,

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

@@ -40,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)) ?

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

@@ -73,6 +73,7 @@
#define RILMODEM_DEFAULT_DATA_CALL_RETRY_LIMIT 4
#define RILMODEM_DEFAULT_DATA_CALL_RETRY_DELAY 200 /* ms */
#define RILMODEM_DEFAULT_EMPTY_PIN_QUERY TRUE /* optimistic */
#define RILMODEM_DEFAULT_LEGACY_IMEI_QUERY FALSE
/*
* The convention is that the keys which can only appear in the [Settings]
@@ -105,6 +106,7 @@
#define RILCONF_DATA_CALL_RETRY_DELAY "dataCallRetryDelay"
#define RILCONF_LOCAL_HANGUP_REASONS "localHangupReasons"
#define RILCONF_REMOTE_HANGUP_REASONS "remoteHangupReasons"
#define RILCONF_DEFAULT_LEGACY_IMEI_QUERY "legacyImeiQuery"
/* Modem error ids */
#define RIL_ERROR_ID_RILD_RESTART "rild-restart"
@@ -183,6 +185,7 @@ typedef struct sailfish_slot_impl {
struct ril_sim_settings *sim_settings;
struct ril_oem_raw *oem_raw;
struct ril_data *data;
gboolean legacy_imei_query;
guint start_timeout;
guint start_timeout_id;
MceDisplay *display;
@@ -440,6 +443,99 @@ static void ril_plugin_check_ready(ril_slot *slot)
}
}
static void ril_plugin_get_imeisv_cb(GRilIoChannel *io, int status,
const void *data, guint len, void *user_data)
{
ril_slot *slot = user_data;
char *imeisv = NULL;
GASSERT(slot->imei_req_id);
slot->imei_req_id = 0;
if (status == RIL_E_SUCCESS) {
GRilIoParser rilp;
grilio_parser_init(&rilp, data, len);
imeisv = grilio_parser_get_utf8(&rilp);
DBG("%s", imeisv);
/*
* slot->imei should be either NULL (when we get connected
* to rild the very first time) or match the already known
* IMEI (if rild crashed and we have reconnected)
*/
if (slot->imeisv && imeisv && strcmp(slot->imeisv, imeisv)) {
ofono_warn("IMEISV has changed \"%s\" -> \"%s\"",
slot->imeisv, imeisv);
}
} else {
ofono_error("Slot %u IMEISV query error: %s",
slot->config.slot, ril_error_to_string(status));
}
if (slot->imeisv) {
/* We assume that IMEISV never changes */
g_free(imeisv);
} else {
slot->imeisv = (imeisv ? imeisv : g_strdup(""));
sailfish_manager_imeisv_obtained(slot->handle, slot->imeisv);
}
ril_plugin_check_modem(slot);
}
static void ril_plugin_get_imei_cb(GRilIoChannel *io, int status,
const void *data, guint len, void *user_data)
{
ril_slot *slot = user_data;
char *imei = NULL;
GASSERT(slot->imei_req_id);
slot->imei_req_id = 0;
if (status == RIL_E_SUCCESS) {
GRilIoParser rilp;
grilio_parser_init(&rilp, data, len);
imei = grilio_parser_get_utf8(&rilp);
DBG("%s", imei);
/*
* slot->imei should be either NULL (when we get connected
* to rild the very first time) or match the already known
* IMEI (if rild crashed and we have reconnected)
*/
if (slot->imei && imei && strcmp(slot->imei, imei)) {
ofono_warn("IMEI has changed \"%s\" -> \"%s\"",
slot->imei, imei);
}
if (imei) {
/* IMEI query was successful, fetch IMEISV too */
GRilIoRequest *req = grilio_request_new();
slot->imei_req_id =
grilio_channel_send_request_full(slot->io,
req, RIL_REQUEST_GET_IMEISV,
ril_plugin_get_imeisv_cb, NULL, slot);
grilio_request_unref(req);
}
} else {
ofono_error("Slot %u IMEI query error: %s", slot->config.slot,
ril_error_to_string(status));
}
if (slot->imei) {
/* We assume that IMEI never changes */
g_free(imei);
} else {
slot->imei = imei ? imei : g_strdup_printf("%d", slot->index);
sailfish_manager_imei_obtained(slot->handle, slot->imei);
}
ril_plugin_check_modem(slot);
ril_plugin_check_ready(slot);
}
static void ril_plugin_device_identity_cb(GRilIoChannel *io, int status,
const void *data, guint len, void *user_data)
{
@@ -505,6 +601,28 @@ static void ril_plugin_device_identity_cb(GRilIoChannel *io, int status,
ril_plugin_check_ready(slot);
}
static void ril_plugin_start_imei_query(ril_slot *slot, gboolean blocking,
int retries)
{
GRilIoRequest *req = grilio_request_new();
/* There was a bug in libgrilio which was making request blocking
* regardless of what we pass to grilio_request_set_blocking(),
* that's why we don't call grilio_request_set_blocking() if
* blocking is FALSE */
if (blocking) grilio_request_set_blocking(req, TRUE);
grilio_request_set_retry(req, RIL_RETRY_MS, retries);
grilio_channel_cancel_request(slot->io, slot->imei_req_id, FALSE);
slot->imei_req_id = (slot->legacy_imei_query ?
grilio_channel_send_request_full(slot->io, req,
RIL_REQUEST_GET_IMEI,
ril_plugin_get_imei_cb, NULL, slot) :
grilio_channel_send_request_full(slot->io, req,
RIL_REQUEST_DEVICE_IDENTITY,
ril_plugin_device_identity_cb, NULL, slot));
grilio_request_unref(req);
}
static enum sailfish_sim_state ril_plugin_sim_state(ril_slot *slot)
{
const struct ril_sim_card_status *status = slot->sim_card->status;
@@ -551,21 +669,11 @@ static void ril_plugin_sim_state_changed(struct ril_sim_card *card, void *data)
* (this time, limited number).
*
* Some RILs fail RIL_REQUEST_DEVICE_IDENTITY until
* the modem hasn't been properly initialized.
* the modem has been properly initialized.
*/
GRilIoRequest *req = grilio_request_new();
DBG("Giving slot %u last chance", slot->config.slot);
grilio_request_set_retry(req, RIL_RETRY_MS,
ril_plugin_start_imei_query(slot, FALSE,
RIL_DEVICE_IDENTITY_RETRIES_LAST);
grilio_channel_cancel_request(slot->io,
slot->imei_req_id, FALSE);
slot->imei_req_id =
grilio_channel_send_request_full(slot->io,
req, RIL_REQUEST_DEVICE_IDENTITY,
ril_plugin_device_identity_cb,
NULL, slot);
grilio_request_unref(req);
}
slot->received_sim_status = TRUE;
}
@@ -814,7 +922,6 @@ static void ril_plugin_slot_connected(ril_slot *slot)
ril_plugin *plugin = slot->plugin;
const struct ril_plugin_settings *ps = &plugin->settings;
const char *log_prefix = ril_plugin_log_prefix(slot);
GRilIoRequest *req;
ofono_debug("%s version %u", (slot->name && slot->name[0]) ?
slot->name : "RIL", slot->io->ril_version);
@@ -826,19 +933,11 @@ static void ril_plugin_slot_connected(ril_slot *slot)
* Modem will be registered after RIL_REQUEST_DEVICE_IDENTITY
* successfully completes. By the time ofono starts, rild may
* not be completely functional. Waiting until it responds to
* RIL_REQUEST_DEVICE_IDENTITY (and retrying the request on
* failure) gives rild time to finish whatever it's doing during
* initialization.
* RIL_REQUEST_DEVICE_IDENTITY (or RIL_REQUEST_GET_IMEI/SV)
* and retrying the request on failure, (hopefully) gives rild
* enough time to finish whatever it's doing during initialization.
*/
GASSERT(!slot->imei_req_id);
req = grilio_request_new();
/* Don't allow any other requests while this one is pending */
grilio_request_set_blocking(req, TRUE);
grilio_request_set_retry(req, RIL_RETRY_MS, -1);
slot->imei_req_id = grilio_channel_send_request_full(slot->io,
req, RIL_REQUEST_DEVICE_IDENTITY,
ril_plugin_device_identity_cb, NULL, slot);
grilio_request_unref(req);
ril_plugin_start_imei_query(slot, TRUE, -1);
GASSERT(!slot->radio);
slot->radio = ril_radio_new(slot->io);
@@ -1051,6 +1150,7 @@ static ril_slot *ril_plugin_slot_new_take(char *sockpath, char *path,
slot->config.enable_voicecall = RILMODEM_DEFAULT_ENABLE_VOICECALL;
slot->timeout = RILMODEM_DEFAULT_TIMEOUT;
slot->sim_flags = RILMODEM_DEFAULT_SIM_FLAGS;
slot->legacy_imei_query = RILMODEM_DEFAULT_LEGACY_IMEI_QUERY;
slot->start_timeout = RILMODEM_DEFAULT_START_TIMEOUT;
slot->data_opt.allow_data = RILMODEM_DEFAULT_DATA_OPT;
slot->data_opt.data_call_format = RILMODEM_DEFAULT_DATA_CALL_FORMAT;
@@ -1295,6 +1395,14 @@ static ril_slot *ril_plugin_parse_config_group(GKeyFile *file,
g_free(sval);
}
/* legacyImeiQuery */
if (ril_config_get_boolean(file, group,
RILCONF_DEFAULT_LEGACY_IMEI_QUERY,
&slot->legacy_imei_query)) {
DBG("%s: " RILCONF_DEFAULT_LEGACY_IMEI_QUERY " %s", group,
slot->legacy_imei_query ? "on" : "off");
}
return slot;
}

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

@@ -194,3 +194,11 @@ socket=/dev/socket/rild
# 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

@@ -335,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;
@@ -436,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

@@ -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

@@ -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

@@ -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

@@ -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,

246
ofono/src/dbus-queue.c Normal file
View File

@@ -0,0 +1,246 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 Jolla Ltd. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "dbus-queue.h"
#include <gdbus.h>
#include "ofono.h"
struct ofono_dbus_queue {
struct ofono_dbus_queue_request *requests;
};
struct ofono_dbus_queue_request {
struct ofono_dbus_queue_request *next;
ofono_dbus_cb_t fn;
DBusMessage *msg;
void *data;
};
struct ofono_dbus_queue *__ofono_dbus_queue_new()
{
return g_new0(struct ofono_dbus_queue, 1);
}
static struct ofono_dbus_queue_request *__ofono_dbus_queue_req_new
(ofono_dbus_cb_t fn, DBusMessage *msg, void *data)
{
struct ofono_dbus_queue_request *req =
g_slice_new0(struct ofono_dbus_queue_request);
req->msg = dbus_message_ref(msg);
req->data = data;
req->fn = fn;
return req;
}
static void __ofono_dbus_queue_req_free(struct ofono_dbus_queue_request *req)
{
g_slice_free1(sizeof(*req), req);
}
static void __ofono_dbus_queue_req_complete
(struct ofono_dbus_queue_request *req,
ofono_dbus_cb_t fn, void *param)
{
DBusMessage *reply = fn(req->msg, param);
if (!reply)
reply = __ofono_error_failed(req->msg);
__ofono_dbus_pending_reply(&req->msg, reply);
__ofono_dbus_queue_req_free(req);
}
void __ofono_dbus_queue_free(struct ofono_dbus_queue *q)
{
if (q) {
while (q->requests) {
struct ofono_dbus_queue_request *req = q->requests;
DBusMessage *reply = __ofono_error_canceled(req->msg);
__ofono_dbus_pending_reply(&req->msg, reply);
q->requests = req->next;
__ofono_dbus_queue_req_free(req);
}
g_free(q);
}
}
ofono_bool_t __ofono_dbus_queue_pending(struct ofono_dbus_queue *q)
{
return q && q->requests;
}
ofono_bool_t __ofono_dbus_queue_set_pending(struct ofono_dbus_queue *q,
DBusMessage *msg)
{
if (!q || q->requests)
return FALSE;
q->requests = __ofono_dbus_queue_req_new(NULL, msg, NULL);
return TRUE;
}
void __ofono_dbus_queue_request(struct ofono_dbus_queue *q,
ofono_dbus_cb_t fn, DBusMessage *msg, void *data)
{
struct ofono_dbus_queue_request *req =
__ofono_dbus_queue_req_new(fn, msg, data);
if (q->requests) {
struct ofono_dbus_queue_request *prev = q->requests;
while (prev->next)
prev = prev->next;
prev->next = req;
} else {
DBusMessage *reply;
q->requests = req;
reply = req->fn(req->msg, req->data);
if (reply) {
/* The request has completed synchronously */
__ofono_dbus_queue_reply_msg(q, reply);
}
}
}
/* Consumes one reference to the reply */
void __ofono_dbus_queue_reply_msg(struct ofono_dbus_queue *q,
DBusMessage *reply)
{
struct ofono_dbus_queue_request *done, *next;
if (!q || !q->requests) {
/* This should never happen */
dbus_message_unref(reply);
return;
}
/* De-queue the request */
done = q->requests;
next = done->next;
q->requests = next;
done->next = NULL;
/* Interpret NULL reply as a cancel */
if (!reply)
reply = __ofono_error_canceled(done->msg);
/* Send the reply */
__ofono_dbus_pending_reply(&done->msg, reply);
__ofono_dbus_queue_req_free(done);
/* Submit the next request if there is any */
if (next) {
next->fn(next->msg, next->data);
}
}
void __ofono_dbus_queue_reply_ok(struct ofono_dbus_queue *q)
{
__ofono_dbus_queue_reply_fn(q, dbus_message_new_method_return);
}
void __ofono_dbus_queue_reply_failed(struct ofono_dbus_queue *q)
{
__ofono_dbus_queue_reply_fn(q, __ofono_error_failed);
}
void __ofono_dbus_queue_reply_fn(struct ofono_dbus_queue *q,
ofono_dbus_reply_cb_t fn)
{
if (q && q->requests)
__ofono_dbus_queue_reply_msg(q, fn(q->requests->msg));
}
void __ofono_dbus_queue_reply_all_ok(struct ofono_dbus_queue *q)
{
__ofono_dbus_queue_reply_all_fn(q, dbus_message_new_method_return);
}
void __ofono_dbus_queue_reply_all_failed(struct ofono_dbus_queue *q)
{
__ofono_dbus_queue_reply_all_fn(q, __ofono_error_failed);
}
static DBusMessage * __ofono_dbus_queue_reply_all_wrapper(DBusMessage *msg,
void *data)
{
return ((ofono_dbus_reply_cb_t)data)(msg);
}
void __ofono_dbus_queue_reply_all_fn(struct ofono_dbus_queue *q,
ofono_dbus_reply_cb_t fn)
{
__ofono_dbus_queue_reply_all_fn_param(q,
__ofono_dbus_queue_reply_all_wrapper, fn);
}
void __ofono_dbus_queue_reply_all_fn_param(struct ofono_dbus_queue *q,
ofono_dbus_cb_t fn, void *param)
{
struct ofono_dbus_queue_request *prev, *req;
ofono_dbus_cb_t handler;
void *data;
if (!q || !q->requests)
return;
/* Store handler and data so that we can compare against them */
req = q->requests;
handler = req->fn;
data = req->data;
/* De-queue the first request */
q->requests = req->next;
req->next = NULL;
/* Send the reply and free the request */
__ofono_dbus_queue_req_complete(req, fn, param);
/*
* Find all other requests with the same handler and the same data
* and complete those too (except when the handler is NULL)
*/
if (!handler)
return;
prev = NULL;
req = q->requests;
while (req) {
struct ofono_dbus_queue_request *next = req->next;
if (req->fn == handler && req->data == data) {
/* Found a match */
if (prev) {
prev->next = next;
} else {
q->requests = next;
}
__ofono_dbus_queue_req_complete(req, fn, param);
} else {
/* Keep this one */
prev = req;
}
req = next;
}
}

47
ofono/src/dbus-queue.h Normal file
View File

@@ -0,0 +1,47 @@
/*
* oFono - Open Source Telephony
*
* Copyright (C) 2017 Jolla Ltd. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#ifndef OFONO_DBUS_QUEUE_H
#define OFONO_DBUS_QUEUE_H
#include <ofono/types.h>
#include <ofono/dbus.h>
struct ofono_dbus_queue;
typedef DBusMessage * (* ofono_dbus_cb_t) (DBusMessage *msg, void *data);
typedef DBusMessage * (* ofono_dbus_reply_cb_t) (DBusMessage *msg);
struct ofono_dbus_queue *__ofono_dbus_queue_new(void);
void __ofono_dbus_queue_free(struct ofono_dbus_queue *q);
void __ofono_dbus_queue_request(struct ofono_dbus_queue *q,
ofono_dbus_cb_t fn, DBusMessage *msg, void *data);
ofono_bool_t __ofono_dbus_queue_pending(struct ofono_dbus_queue *q);
ofono_bool_t __ofono_dbus_queue_set_pending(struct ofono_dbus_queue *q,
DBusMessage *msg);
void __ofono_dbus_queue_reply_msg(struct ofono_dbus_queue *q,
DBusMessage *reply);
void __ofono_dbus_queue_reply_ok(struct ofono_dbus_queue *q);
void __ofono_dbus_queue_reply_failed(struct ofono_dbus_queue *q);
void __ofono_dbus_queue_reply_fn(struct ofono_dbus_queue *q,
ofono_dbus_reply_cb_t fn);
void __ofono_dbus_queue_reply_all_ok(struct ofono_dbus_queue *q);
void __ofono_dbus_queue_reply_all_failed(struct ofono_dbus_queue *q);
void __ofono_dbus_queue_reply_all_fn(struct ofono_dbus_queue *q,
ofono_dbus_reply_cb_t fn);
void __ofono_dbus_queue_reply_all_fn_param(struct ofono_dbus_queue *q,
ofono_dbus_cb_t fn, void *data);
#endif /* OFONO_DBUS_QUEUE_H */

View File

@@ -326,7 +326,7 @@ static gboolean assign_context(struct pri_context *ctx, int use_cid)
} else
ctx->context.cid = gprs_cid_alloc(ctx->gprs);
if (ctx->context.cid == 0)
if (ctx->context.cid > idmap_get_max(cidmap))
return FALSE;
for (l = ctx->gprs->context_drivers; l; l = l->next) {
@@ -1220,11 +1220,30 @@ static void pri_deactivate_callback(const struct ofono_error *error, void *data)
}
}
static void gprs_set_attached_property(struct ofono_gprs *gprs,
ofono_bool_t attached)
{
const char *path;
DBusConnection *conn = ofono_dbus_get_connection();
dbus_bool_t value = attached;
if (gprs->attached == attached)
return;
gprs->attached = attached;
path = __ofono_atom_get_path(gprs->atom);
ofono_dbus_signal_property_changed(conn, path,
OFONO_CONNECTION_MANAGER_INTERFACE,
"Attached", DBUS_TYPE_BOOLEAN, &value);
}
static void pri_read_settings_callback(const struct ofono_error *error,
void *data)
{
struct pri_context *pri_ctx = data;
struct ofono_gprs_context *gc = pri_ctx->context_driver;
struct ofono_gprs *gprs = pri_ctx->gprs;
DBusConnection *conn = ofono_dbus_get_connection();
dbus_bool_t value;
@@ -1248,9 +1267,20 @@ static void pri_read_settings_callback(const struct ofono_error *error,
}
value = pri_ctx->active;
gprs->flags &= !GPRS_FLAG_ATTACHING;
gprs->driver_attached = TRUE;
gprs_set_attached_property(gprs, TRUE);
ofono_dbus_signal_property_changed(conn, pri_ctx->path,
OFONO_CONNECTION_CONTEXT_INTERFACE,
"Active", DBUS_TYPE_BOOLEAN, &value);
if (gprs->flags & GPRS_FLAG_RECHECK) {
gprs->flags &= ~GPRS_FLAG_RECHECK;
gprs_netreg_update(gprs);
}
}
static DBusMessage *pri_set_apn(struct pri_context *ctx, DBusConnection *conn,
@@ -1859,24 +1889,6 @@ static void release_active_contexts(struct ofono_gprs *gprs)
}
}
static void gprs_set_attached_property(struct ofono_gprs *gprs,
ofono_bool_t attached)
{
const char *path;
DBusConnection *conn = ofono_dbus_get_connection();
dbus_bool_t value = attached;
if (gprs->attached == attached)
return;
gprs->attached = attached;
path = __ofono_atom_get_path(gprs->atom);
ofono_dbus_signal_property_changed(conn, path,
OFONO_CONNECTION_MANAGER_INTERFACE,
"Attached", DBUS_TYPE_BOOLEAN, &value);
}
static void gprs_attached_update(struct ofono_gprs *gprs)
{
ofono_bool_t attached;
@@ -1980,11 +1992,12 @@ static void gprs_netreg_update(struct ofono_gprs *gprs)
DBG("attach: %u, driver_attached: %u", attach, gprs->driver_attached);
if (ofono_netreg_get_technology(gprs->netreg) ==
ACCESS_TECHNOLOGY_EUTRAN) {
/* Ignore attach logic for LTE. There is no such concept. */
gprs_set_attached_property(gprs, attach);
return;
}
ACCESS_TECHNOLOGY_EUTRAN)
/*
* For LTE we set attached status only on successful
* context activation.
*/
return;
if (gprs->driver_attached == attach)
return;
@@ -2290,6 +2303,14 @@ void ofono_gprs_cid_activated(struct ofono_gprs *gprs, unsigned int cid,
pri_set_apn(pri_ctx, conn, NULL, apn);
}
/* Prevent ofono_gprs_status_notify from changing the 'attached'
* state until after the context has been set to 'active' in
* the pri_read_settings_callback; this prevents a race where
* the connection manager sees the modem as attached before there
* is an active context.
*/
gprs->flags |= GPRS_FLAG_ATTACHING;
gc->driver->read_settings(gc, cid, pri_read_settings_callback, pri_ctx);
}
@@ -3745,7 +3766,7 @@ void *ofono_gprs_get_data(struct ofono_gprs *gprs)
return gprs->driver_data;
}
ofono_bool_t ofono_gprs_get_roaming_allowed(struct ofono_gprs *gprs)
gboolean __ofono_gprs_get_roaming_allowed(struct ofono_gprs *gprs)
{
return gprs->roaming_allowed;
}

View File

@@ -326,6 +326,13 @@ static gboolean sco_connect_cb(GIOChannel *io, GIOCondition cond,
sk = g_io_channel_unix_get_fd(io);
if (card->msg && dbus_message_has_member(card->msg, "Acquire")) {
reply = g_dbus_create_reply(card->msg, DBUS_TYPE_UNIX_FD, &sk,
DBUS_TYPE_BYTE, &card->selected_codec,
DBUS_TYPE_INVALID);
goto done;
}
send_new_connection(card->path, sk, card->selected_codec);
close(sk);
@@ -403,6 +410,9 @@ static const GDBusMethodTable card_methods[] = {
NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
card_get_properties) },
{ GDBUS_ASYNC_METHOD("Connect", NULL, NULL, card_connect) },
{ GDBUS_ASYNC_METHOD("Acquire", NULL,
GDBUS_ARGS({"sco", "h"}, {"codec", "y"}),
card_connect) },
{ }
};

377
ofono/src/lte.c Normal file
View File

@@ -0,0 +1,377 @@
/*
*
* 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
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <errno.h>
#include <glib.h>
#include <gdbus.h>
#include "ofono.h"
#include "common.h"
#include "storage.h"
#define SETTINGS_STORE "lte"
#define SETTINGS_GROUP "Settings"
#define DEFAULT_APN_KEY "DefaultAccessPointName"
struct ofono_lte {
const struct ofono_lte_driver *driver;
void *driver_data;
struct ofono_atom *atom;
char *imsi;
GKeyFile *settings;
DBusMessage *pending;
struct ofono_lte_default_attach_info pending_info;
struct ofono_lte_default_attach_info info;
};
static GSList *g_drivers = NULL;
static void lte_load_settings(struct ofono_lte *lte)
{
char *apn;
if (lte->imsi == NULL)
return;
lte->settings = storage_open(lte->imsi, SETTINGS_STORE);
if (lte->settings == NULL) {
ofono_error("LTE: Can't open settings file, "
"changes won't be persistent");
return;
}
apn = g_key_file_get_string(lte->settings, SETTINGS_GROUP ,
DEFAULT_APN_KEY, NULL);
if (apn) {
strcpy(lte->info.apn, apn);
g_free(apn);
}
}
static DBusMessage *lte_get_properties(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ofono_lte *lte = data;
const char *apn = lte->info.apn;
DBusMessage *reply;
DBusMessageIter iter;
DBusMessageIter dict;
reply = dbus_message_new_method_return(msg);
if (reply == NULL)
return NULL;
dbus_message_iter_init_append(reply, &iter);
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
OFONO_PROPERTIES_ARRAY_SIGNATURE,
&dict);
ofono_dbus_dict_append(&dict, DEFAULT_APN_KEY, DBUS_TYPE_STRING, &apn);
dbus_message_iter_close_container(&iter, &dict);
return reply;
}
static void lte_set_default_attach_info_cb(const struct ofono_error *error,
void *data)
{
struct ofono_lte *lte = data;
const char *path = __ofono_atom_get_path(lte->atom);
DBusConnection *conn = ofono_dbus_get_connection();
DBusMessage *reply;
const char *apn = lte->info.apn;
DBG("%s error %d", path, error->type);
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
__ofono_dbus_pending_reply(&lte->pending,
__ofono_error_failed(lte->pending));
return;
}
g_strlcpy(lte->info.apn, lte->pending_info.apn,
OFONO_GPRS_MAX_APN_LENGTH + 1);
if (lte->settings) {
if (strlen(lte->info.apn) == 0)
/* Clear entry on empty APN. */
g_key_file_remove_key(lte->settings, SETTINGS_GROUP,
DEFAULT_APN_KEY, NULL);
else
g_key_file_set_string(lte->settings, SETTINGS_GROUP,
DEFAULT_APN_KEY, lte->info.apn);
storage_sync(lte->imsi, SETTINGS_STORE, lte->settings);
}
reply = dbus_message_new_method_return(lte->pending);
__ofono_dbus_pending_reply(&lte->pending, reply);
ofono_dbus_signal_property_changed(conn, path,
OFONO_CONNECTION_CONTEXT_INTERFACE,
DEFAULT_APN_KEY,
DBUS_TYPE_STRING, &apn);
}
static DBusMessage *lte_set_default_apn(struct ofono_lte *lte,
DBusConnection *conn, DBusMessage *msg,
const char *apn)
{
if (lte->driver->set_default_attach_info == NULL)
return __ofono_error_not_implemented(msg);
if (lte->pending)
return __ofono_error_busy(msg);
if (strlen(apn) > OFONO_GPRS_MAX_APN_LENGTH)
return __ofono_error_invalid_format(msg);
if (g_str_equal(apn, lte->info.apn))
return dbus_message_new_method_return(msg);
/* We do care about empty value: it can be used for reset. */
if (is_valid_apn(apn) == FALSE && apn[0] != '\0')
return __ofono_error_invalid_format(msg);
lte->pending = dbus_message_ref(msg);
g_strlcpy(lte->pending_info.apn, apn, OFONO_GPRS_MAX_APN_LENGTH + 1);
lte->driver->set_default_attach_info(lte, &lte->pending_info,
lte_set_default_attach_info_cb, lte);
return NULL;
}
static DBusMessage *lte_set_property(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ofono_lte *lte = data;
DBusMessageIter iter;
DBusMessageIter var;
const char *property;
const char *str;
if (!dbus_message_iter_init(msg, &iter))
return __ofono_error_invalid_args(msg);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
return __ofono_error_invalid_args(msg);
dbus_message_iter_get_basic(&iter, &property);
dbus_message_iter_next(&iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
return __ofono_error_invalid_args(msg);
dbus_message_iter_recurse(&iter, &var);
if (!strcmp(property, DEFAULT_APN_KEY)) {
if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
return __ofono_error_invalid_args(msg);
dbus_message_iter_get_basic(&var, &str);
return lte_set_default_apn(lte, conn, msg, str);
}
return __ofono_error_invalid_args(msg);
}
static const GDBusMethodTable lte_methods[] = {
{ GDBUS_METHOD("GetProperties",
NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
lte_get_properties) },
{ GDBUS_ASYNC_METHOD("SetProperty",
GDBUS_ARGS({ "property", "s" }, { "value", "v" }),
NULL, lte_set_property) },
{ }
};
static const GDBusSignalTable lte_signals[] = {
{ GDBUS_SIGNAL("PropertyChanged",
GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
{ }
};
static void lte_atom_remove(struct ofono_atom *atom)
{
struct ofono_lte *lte = __ofono_atom_get_data(atom);
DBG("atom: %p", atom);
if (lte == NULL)
return;
if (lte->settings) {
storage_close(lte->imsi, SETTINGS_STORE, lte->settings, TRUE);
lte->settings = NULL;
}
if (lte->driver && lte->driver->remove)
lte->driver->remove(lte);
g_free(lte->imsi);
lte->imsi = NULL;
g_free(lte);
}
struct ofono_lte *ofono_lte_create(struct ofono_modem *modem,
const char *driver, void *data)
{
struct ofono_lte *lte;
GSList *l;
if (driver == NULL)
return NULL;
lte = g_try_new0(struct ofono_lte, 1);
if (lte == NULL)
return NULL;
lte->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_LTE,
lte_atom_remove, lte);
for (l = g_drivers; l; l = l->next) {
const struct ofono_lte_driver *drv = l->data;
if (g_strcmp0(drv->name, driver))
continue;
if (drv->probe(lte, data) < 0)
continue;
lte->driver = drv;
break;
}
DBG("LTE atom created");
return lte;
}
int ofono_lte_driver_register(const struct ofono_lte_driver *d)
{
DBG("driver: %p, name: %s", d, d->name);
if (d->probe == NULL)
return -EINVAL;
g_drivers = g_slist_prepend(g_drivers, (void *) d);
return 0;
}
void ofono_lte_driver_unregister(const struct ofono_lte_driver *d)
{
DBG("driver: %p, name: %s", d, d->name);
g_drivers = g_slist_remove(g_drivers, (void *) d);
}
static void lte_atom_unregister(struct ofono_atom *atom)
{
DBusConnection *conn = ofono_dbus_get_connection();
struct ofono_modem *modem = __ofono_atom_get_modem(atom);
const char *path = __ofono_atom_get_path(atom);
ofono_modem_remove_interface(modem, OFONO_LTE_INTERFACE);
g_dbus_unregister_interface(conn, path, OFONO_LTE_INTERFACE);
}
static void ofono_lte_finish_register(struct ofono_lte *lte)
{
DBusConnection *conn = ofono_dbus_get_connection();
struct ofono_modem *modem = __ofono_atom_get_modem(lte->atom);
const char *path = __ofono_atom_get_path(lte->atom);
if (!g_dbus_register_interface(conn, path,
OFONO_LTE_INTERFACE,
lte_methods, lte_signals, NULL,
lte, NULL)) {
ofono_error("could not create %s interface",
OFONO_LTE_INTERFACE);
return;
}
ofono_modem_add_interface(modem, OFONO_LTE_INTERFACE);
__ofono_atom_register(lte->atom, lte_atom_unregister);
}
static void lte_init_default_attach_info_cb(const struct ofono_error *error,
void *data)
{
struct ofono_lte *lte = data;
ofono_lte_finish_register(lte);
}
void ofono_lte_register(struct ofono_lte *lte)
{
struct ofono_modem *modem = __ofono_atom_get_modem(lte->atom);
struct ofono_sim *sim = __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem);
const char *imsi = ofono_sim_get_imsi(sim);
if (imsi == NULL) {
ofono_error("No sim atom required for registering LTE atom.");
return;
}
lte->imsi = g_strdup(imsi);
lte_load_settings(lte);
if (lte->driver->set_default_attach_info) {
lte->driver->set_default_attach_info(lte, &lte->info,
lte_init_default_attach_info_cb, lte);
return;
}
ofono_lte_finish_register(lte);
}
void ofono_lte_remove(struct ofono_lte *lte)
{
__ofono_atom_free(lte->atom);
}
void ofono_lte_set_data(struct ofono_lte *lte, void *data)
{
lte->driver_data = data;
}
void *ofono_lte_get_data(const struct ofono_lte *lte)
{
return lte->driver_data;
}

View File

@@ -762,8 +762,12 @@ static DBusMessage *set_property_online(struct ofono_modem *modem,
if (ofono_modem_get_emergency_mode(modem) == TRUE)
return __ofono_error_emergency_active(msg);
if (modem_is_always_online(modem) == TRUE)
return __ofono_error_not_implemented(msg);
if (modem_is_always_online(modem) == TRUE) {
if (online)
return dbus_message_new_method_return(msg);
else
return __ofono_error_not_implemented(msg);
}
modem->pending = dbus_message_ref(msg);

View File

@@ -60,7 +60,7 @@ static void mtu_watch_limit_mtu(struct mtu_watch *self)
}
static void mtu_watch_handle_rtattr(struct mtu_watch *self,
const struct rtattr *rta, int len)
const struct rtattr *rta, unsigned int len)
{
int mtu = 0;
const char *ifname = NULL;
@@ -82,7 +82,7 @@ static void mtu_watch_handle_rtattr(struct mtu_watch *self,
}
static void mtu_watch_handle_ifinfomsg(struct mtu_watch *self,
const struct ifinfomsg *ifi, int len)
const struct ifinfomsg *ifi, unsigned int len)
{
if (ifi->ifi_flags & IFF_UP) {
const struct rtattr *rta = IFLA_RTA(ifi);
@@ -92,7 +92,7 @@ static void mtu_watch_handle_ifinfomsg(struct mtu_watch *self,
}
static void mtu_watch_handle_nlmsg(struct mtu_watch *self,
const struct nlmsghdr *hdr, int len)
const struct nlmsghdr *hdr, unsigned int len)
{
while (len > 0 && NLMSG_OK(hdr, len)) {
if (hdr->nlmsg_type == RTM_NEWLINK) {

View File

@@ -76,8 +76,8 @@ void ofono_netmon_serving_cell_notify(struct ofono_netmon *netmon,
DBusMessageIter dict;
enum ofono_netmon_info next_info_type = info_type;
const char *technology = cell_type_to_tech_name(type);
char *mcc = NULL;
char *mnc = NULL;
char *mcc;
char *mnc;
int intval;
netmon->reply = dbus_message_new_method_return(netmon->pending);
@@ -95,7 +95,8 @@ void ofono_netmon_serving_cell_notify(struct ofono_netmon *netmon,
if (technology == NULL)
goto done;
ofono_dbus_dict_append(&dict, "Technology", DBUS_TYPE_STRING, &technology);
ofono_dbus_dict_append(&dict, "Technology",
DBUS_TYPE_STRING, &technology);
while (next_info_type != OFONO_NETMON_INFO_INVALID) {
switch (next_info_type) {
@@ -180,6 +181,57 @@ void ofono_netmon_serving_cell_notify(struct ofono_netmon *netmon,
intval, uint8_t, DBUS_TYPE_BYTE);
break;
case OFONO_NETMON_INFO_RSCP:
intval = va_arg(arglist, int);
CELL_INFO_DICT_APPEND(&dict, "ReceivedSignalCodePower",
intval, uint8_t, DBUS_TYPE_BYTE);
break;
case OFONO_NETMON_INFO_ECN0:
intval = va_arg(arglist, int);
CELL_INFO_DICT_APPEND(&dict, "ReceivedEnergyRatio",
intval, uint8_t, DBUS_TYPE_BYTE);
break;
case OFONO_NETMON_INFO_RSRQ:
intval = va_arg(arglist, int);
CELL_INFO_DICT_APPEND(&dict,
"ReferenceSignalReceivedQuality",
intval, uint8_t, DBUS_TYPE_BYTE);
break;
case OFONO_NETMON_INFO_RSRP:
intval = va_arg(arglist, int);
CELL_INFO_DICT_APPEND(&dict,
"ReferenceSignalReceivedPower",
intval, uint8_t, DBUS_TYPE_BYTE);
break;
case OFONO_NETMON_INFO_EARFCN:
intval = va_arg(arglist, int);
CELL_INFO_DICT_APPEND(&dict, "EARFCN",
intval, uint16_t, DBUS_TYPE_UINT16);
break;
case OFONO_NETMON_INFO_EBAND:
intval = va_arg(arglist, int);
CELL_INFO_DICT_APPEND(&dict, "EBand",
intval, uint8_t, DBUS_TYPE_BYTE);
break;
case OFONO_NETMON_INFO_CQI:
intval = va_arg(arglist, int);
CELL_INFO_DICT_APPEND(&dict, "ChannelQualityIndicator",
intval, uint8_t, DBUS_TYPE_BYTE);
break;
case OFONO_NETMON_INFO_INVALID:
break;
}

View File

@@ -36,6 +36,7 @@
#include "simutil.h"
#include "util.h"
#include "storage.h"
#include "dbus-queue.h"
#define SETTINGS_STORE "netreg"
#define SETTINGS_GROUP "Settings"
@@ -61,9 +62,7 @@ struct ofono_netreg {
GSList *operator_list;
struct ofono_network_registration_ops *ops;
int flags;
DBusMessage *pending;
GSList *pending_auto;
GSList *pending_list;
struct ofono_dbus_queue *q;
int signal_strength;
struct sim_spdi *spdi;
struct sim_eons *eons;
@@ -219,14 +218,11 @@ static void set_registration_mode(struct ofono_netreg *netreg, int mode)
static void register_callback(const struct ofono_error *error, void *data)
{
struct ofono_netreg *netreg = data;
DBusMessage *reply;
if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
reply = dbus_message_new_method_return(netreg->pending);
__ofono_dbus_queue_reply_ok(netreg->q);
else
reply = __ofono_error_failed(netreg->pending);
__ofono_dbus_pending_reply(&netreg->pending, reply);
__ofono_dbus_queue_reply_failed(netreg->q);
if (netreg->driver->registration_status == NULL)
return;
@@ -601,13 +597,11 @@ static DBusMessage *network_operator_register(DBusConnection *conn,
if (netreg->mode == NETWORK_REGISTRATION_MODE_AUTO_ONLY)
return __ofono_error_access_denied(msg);
if (netreg->pending || netreg->pending_auto || netreg->pending_list)
return __ofono_error_busy(msg);
if (netreg->driver->register_manual == NULL)
return __ofono_error_not_implemented(msg);
netreg->pending = dbus_message_ref(msg);
if (!__ofono_dbus_queue_set_pending(netreg->q, msg))
return __ofono_error_busy(msg);
netreg->driver->register_manual(netreg, opd->mcc, opd->mnc,
register_callback, netreg);
@@ -855,42 +849,13 @@ static DBusMessage *network_get_properties(DBusConnection *conn,
return reply;
}
static void network_reply_ok(gpointer data)
{
DBusMessage *msg = data;
__ofono_dbus_pending_reply(&msg, dbus_message_new_method_return(msg));
}
static void network_reply_failed(gpointer data)
{
DBusMessage *msg = data;
__ofono_dbus_pending_reply(&msg, __ofono_error_failed(msg));
}
static void network_reply_canceled(gpointer data)
{
DBusMessage *msg = data;
__ofono_dbus_pending_reply(&msg, __ofono_error_canceled(msg));
}
static void register_auto_callback(const struct ofono_error *error, void *data)
static DBusMessage *network_register_fn(DBusMessage *msg, void *data)
{
struct ofono_netreg *netreg = data;
if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
g_slist_free_full(netreg->pending_auto, network_reply_ok);
else
g_slist_free_full(netreg->pending_auto, network_reply_failed);
netreg->pending_auto = NULL;
if (netreg->driver->registration_status)
netreg->driver->registration_status(netreg,
registration_status_callback,
netreg);
netreg->driver->register_auto(netreg, register_callback, netreg);
set_registration_mode(netreg, NETWORK_REGISTRATION_MODE_AUTO);
return NULL;
}
static DBusMessage *network_register(DBusConnection *conn,
@@ -901,20 +866,11 @@ static DBusMessage *network_register(DBusConnection *conn,
if (netreg->mode == NETWORK_REGISTRATION_MODE_AUTO_ONLY)
return __ofono_error_access_denied(msg);
if (netreg->pending || netreg->pending_list)
return __ofono_error_busy(msg);
if (netreg->driver->register_auto == NULL)
return __ofono_error_not_implemented(msg);
netreg->pending_auto = g_slist_append(netreg->pending_auto,
dbus_message_ref(msg));
if (!netreg->pending_auto->next) {
netreg->driver->register_auto(netreg, register_auto_callback,
__ofono_dbus_queue_request(netreg->q, network_register_fn, msg,
netreg);
set_registration_mode(netreg, NETWORK_REGISTRATION_MODE_AUTO);
}
return NULL;
}
@@ -989,12 +945,6 @@ static void network_signal_operators_changed(struct ofono_netreg *netreg)
signal = dbus_message_new_signal(path,
OFONO_NETWORK_REGISTRATION_INTERFACE, "OperatorsChanged");
if (signal == NULL) {
ofono_error("Unable to allocate new "
OFONO_NETWORK_REGISTRATION_INTERFACE
".OperatorsChanged signal");
return;
}
dbus_message_iter_init_append(signal, &iter);
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
@@ -1013,10 +963,9 @@ static void network_signal_operators_changed(struct ofono_netreg *netreg)
g_dbus_send_message(conn, signal);
}
static void operator_list_reply(gpointer data, gpointer user_data)
static DBusMessage *operator_list_reply(DBusMessage *msg, gpointer user_data)
{
struct ofono_netreg *netreg = user_data;
DBusMessage *msg = data;
DBusMessage *reply;
DBusMessageIter iter;
DBusMessageIter array;
@@ -1038,7 +987,7 @@ static void operator_list_reply(gpointer data, gpointer user_data)
append_operator_struct_list(netreg, &array);
dbus_message_iter_close_container(&iter, &array);
__ofono_dbus_pending_reply(&msg, reply);
return reply;
}
static void operator_list_callback(const struct ofono_error *error, int total,
@@ -1049,20 +998,25 @@ static void operator_list_callback(const struct ofono_error *error, int total,
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
DBG("Error occurred during operator list");
g_slist_free_full(netreg->pending_list, network_reply_failed);
__ofono_dbus_queue_reply_all_failed(netreg-> q);
} else {
gboolean changed = update_operator_list(netreg, total, list);
g_slist_foreach(netreg->pending_list, operator_list_reply,
netreg);
g_slist_free(netreg->pending_list);
__ofono_dbus_queue_reply_all_fn_param(netreg->q,
operator_list_reply, netreg);
DBG("operator list %schanged", changed ? "" : "not ");
if (changed)
network_signal_operators_changed(netreg);
}
}
netreg->pending_list = NULL;
static DBusMessage *network_scan_cb(DBusMessage *msg, void *data)
{
struct ofono_netreg *netreg = data;
netreg->driver->list_operators(netreg, operator_list_callback, netreg);
return NULL;
}
static DBusMessage *network_scan(DBusConnection *conn,
@@ -1073,17 +1027,10 @@ static DBusMessage *network_scan(DBusConnection *conn,
if (netreg->mode == NETWORK_REGISTRATION_MODE_AUTO_ONLY)
return __ofono_error_access_denied(msg);
if (netreg->pending || netreg->pending_auto)
return __ofono_error_busy(msg);
if (netreg->driver->list_operators == NULL)
return __ofono_error_not_implemented(msg);
netreg->pending_list = g_slist_append(netreg->pending_list,
dbus_message_ref(msg));
if (!netreg->pending_list->next)
netreg->driver->list_operators(netreg, operator_list_callback,
netreg);
__ofono_dbus_queue_request(netreg->q, network_scan_cb, msg, netreg);
return NULL;
}
@@ -1959,14 +1906,7 @@ static void netreg_remove(struct ofono_atom *atom)
if (netreg->driver != NULL && netreg->driver->remove != NULL)
netreg->driver->remove(netreg);
if (netreg->pending) {
__ofono_dbus_pending_reply(&netreg->pending,
__ofono_error_canceled(netreg->pending));
} else if (netreg->pending_auto) {
g_slist_free_full(netreg->pending_auto, network_reply_canceled);
} else if (netreg->pending_list) {
g_slist_free_full(netreg->pending_list, network_reply_canceled);
}
__ofono_dbus_queue_free(netreg->q);
sim_eons_free(netreg->eons);
sim_spdi_free(netreg->spdi);
@@ -2194,6 +2134,7 @@ void ofono_netreg_register(struct ofono_netreg *netreg)
}
netreg->status_watches = __ofono_watchlist_new(g_free);
netreg->q = __ofono_dbus_queue_new();
ofono_modem_add_interface(modem, OFONO_NETWORK_REGISTRATION_INTERFACE);

View File

@@ -160,6 +160,7 @@ enum ofono_atom_type {
OFONO_ATOM_TYPE_HANDSFREE,
OFONO_ATOM_TYPE_SIRI,
OFONO_ATOM_TYPE_NETMON,
OFONO_ATOM_TYPE_LTE,
};
enum ofono_atom_watch_condition {
@@ -272,6 +273,9 @@ gboolean __ofono_call_settings_is_busy(struct ofono_call_settings *cs);
#include <ofono/devinfo.h>
#include <ofono/phonebook.h>
#include <ofono/gprs.h>
gboolean __ofono_gprs_get_roaming_allowed(struct ofono_gprs *gprs);
#include <ofono/gprs-context.h>
#include <ofono/radio-settings.h>
#include <ofono/audio-settings.h>
@@ -590,4 +594,4 @@ int __ofono_sim_mnclength_get_mnclength(const char *imsi);
int mnclength(int mcc, int mnc);
#include <ofono/netmon.h>
#include <ofono/lte.h>

View File

@@ -34,6 +34,7 @@
#include "ofono.h"
#include "common.h"
#include "storage.h"
#include "dbus-queue.h"
#define SETTINGS_STORE "radiosetting"
#define SETTINGS_GROUP "Settings"
@@ -42,8 +43,7 @@
static GSList *g_drivers = NULL;
struct ofono_radio_settings {
DBusMessage *pending;
GSList *pending_get_prop;
struct ofono_dbus_queue *q;
int flags;
enum ofono_radio_access_mode mode;
enum ofono_radio_band_gsm band_gsm;
@@ -280,21 +280,18 @@ static void radio_fast_dormancy_set_callback(const struct ofono_error *error,
void *data)
{
struct ofono_radio_settings *rs = data;
DBusMessage *reply;
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
DBG("Error setting fast dormancy");
rs->fast_dormancy_pending = rs->fast_dormancy;
reply = __ofono_error_failed(rs->pending);
__ofono_dbus_pending_reply(&rs->pending, reply);
__ofono_dbus_queue_reply_failed(rs->q);
return;
}
reply = dbus_message_new_method_return(rs->pending);
__ofono_dbus_pending_reply(&rs->pending, reply);
__ofono_dbus_queue_reply_ok(rs->q);
radio_set_fast_dormancy(rs, rs->fast_dormancy_pending);
}
@@ -344,7 +341,6 @@ static void radio_band_set_callback(const struct ofono_error *error,
void *data)
{
struct ofono_radio_settings *rs = data;
DBusMessage *reply;
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
DBG("Error setting radio frequency band");
@@ -352,14 +348,12 @@ static void radio_band_set_callback(const struct ofono_error *error,
rs->pending_band_gsm = rs->band_gsm;
rs->pending_band_umts = rs->band_umts;
reply = __ofono_error_failed(rs->pending);
__ofono_dbus_pending_reply(&rs->pending, reply);
__ofono_dbus_queue_reply_failed(rs->q);
return;
}
reply = dbus_message_new_method_return(rs->pending);
__ofono_dbus_pending_reply(&rs->pending, reply);
__ofono_dbus_queue_reply_ok(rs->q);
radio_set_band(rs);
}
@@ -394,54 +388,33 @@ static void radio_set_rat_mode(struct ofono_radio_settings *rs,
static void radio_mode_set_callback(const struct ofono_error *error, void *data)
{
struct ofono_radio_settings *rs = data;
DBusMessage *reply;
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
DBG("Error setting radio access mode");
rs->pending_mode = rs->mode;
reply = __ofono_error_failed(rs->pending);
__ofono_dbus_pending_reply(&rs->pending, reply);
__ofono_dbus_queue_reply_failed(rs->q);
return;
}
reply = dbus_message_new_method_return(rs->pending);
__ofono_dbus_pending_reply(&rs->pending, reply);
__ofono_dbus_queue_reply_ok(rs->q);
radio_set_rat_mode(rs, rs->pending_mode);
}
static void radio_send_properties_ok(gpointer data, gpointer user_data)
static DBusMessage *radio_get_properties_reply_cb(DBusMessage *msg, void *data)
{
DBusMessage *msg = data;
struct ofono_radio_settings *rs = user_data;
DBusMessage *reply = radio_get_properties_reply(msg, rs);
__ofono_dbus_pending_reply(&msg, reply);
return radio_get_properties_reply(msg, data);
}
static void radio_send_properties_error(gpointer data, gpointer user_data)
{
DBusMessage *msg = data;
DBusMessage *reply = __ofono_error_failed(msg);
__ofono_dbus_pending_reply(&msg, reply);
}
static void radio_send_properties_reply(struct ofono_radio_settings *rs,
GFunc func)
{
g_slist_foreach(rs->pending_get_prop, func, rs);
g_slist_free(rs->pending_get_prop);
rs->pending_get_prop = NULL;
}
static void radio_send_properties_reply_ok(struct ofono_radio_settings *rs)
static void radio_send_properties_reply(struct ofono_radio_settings *rs)
{
rs->flags |= RADIO_SETTINGS_FLAG_CACHED;
radio_send_properties_reply(rs, radio_send_properties_ok);
__ofono_dbus_queue_reply_all_fn_param(rs->q,
radio_get_properties_reply_cb, rs);
}
static void radio_available_rats_query_callback(const struct ofono_error *error,
@@ -455,14 +428,14 @@ static void radio_available_rats_query_callback(const struct ofono_error *error,
else
DBG("Error while querying available rats");
radio_send_properties_reply_ok(rs);
radio_send_properties_reply(rs);
}
static void radio_query_available_rats(struct ofono_radio_settings *rs)
{
/* Modem technology is not supposed to change, so one query is enough */
if (rs->driver->query_available_rats == NULL || rs->available_rats) {
radio_send_properties_reply_ok(rs);
radio_send_properties_reply(rs);
return;
}
@@ -478,7 +451,8 @@ static void radio_fast_dormancy_query_callback(const struct ofono_error *error,
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
DBG("Error during fast dormancy query");
radio_send_properties_reply(rs, radio_send_properties_error);
__ofono_dbus_queue_reply_failed(rs->q);
return;
}
@@ -507,7 +481,8 @@ static void radio_band_query_callback(const struct ofono_error *error,
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
DBG("Error during radio frequency band query");
radio_send_properties_reply(rs, radio_send_properties_error);
__ofono_dbus_queue_reply_failed(rs->q);
return;
}
@@ -537,7 +512,8 @@ static void radio_rat_mode_query_callback(const struct ofono_error *error,
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
DBG("Error during radio access mode query");
radio_send_properties_reply(rs, radio_send_properties_error);
__ofono_dbus_queue_reply_failed(rs->q);
return;
}
@@ -545,8 +521,7 @@ static void radio_rat_mode_query_callback(const struct ofono_error *error,
radio_query_band(rs);
}
static DBusMessage *radio_get_properties(DBusConnection *conn,
DBusMessage *msg, void *data)
static DBusMessage *radio_get_properties_handler(DBusMessage *msg, void *data)
{
struct ofono_radio_settings *rs = data;
@@ -556,30 +531,18 @@ static DBusMessage *radio_get_properties(DBusConnection *conn,
if (rs->driver->query_rat_mode == NULL)
return __ofono_error_not_implemented(msg);
if (rs->pending_get_prop) {
rs->pending_get_prop = g_slist_append(rs->pending_get_prop,
dbus_message_ref(msg));
} else {
rs->pending_get_prop = g_slist_append(NULL,
dbus_message_ref(msg));
rs->driver->query_rat_mode(rs, radio_rat_mode_query_callback,
rs);
}
rs->driver->query_rat_mode(rs, radio_rat_mode_query_callback, rs);
return NULL;
}
static DBusMessage *radio_set_property(DBusConnection *conn, DBusMessage *msg,
void *data)
static DBusMessage *radio_set_property_handler(DBusMessage *msg, void *data)
{
struct ofono_radio_settings *rs = data;
DBusMessageIter iter;
DBusMessageIter var;
const char *property;
if (rs->pending)
return __ofono_error_busy(msg);
if (!dbus_message_iter_init(msg, &iter))
return __ofono_error_invalid_args(msg);
@@ -611,7 +574,6 @@ static DBusMessage *radio_set_property(DBusConnection *conn, DBusMessage *msg,
if (rs->mode == mode)
return dbus_message_new_method_return(msg);
rs->pending = dbus_message_ref(msg);
rs->pending_mode = mode;
rs->driver->set_rat_mode(rs, mode, radio_mode_set_callback, rs);
@@ -634,7 +596,6 @@ static DBusMessage *radio_set_property(DBusConnection *conn, DBusMessage *msg,
if (rs->band_gsm == band)
return dbus_message_new_method_return(msg);
rs->pending = dbus_message_ref(msg);
rs->pending_band_gsm = band;
rs->driver->set_band(rs, band, rs->band_umts,
@@ -658,7 +619,6 @@ static DBusMessage *radio_set_property(DBusConnection *conn, DBusMessage *msg,
if (rs->band_umts == band)
return dbus_message_new_method_return(msg);
rs->pending = dbus_message_ref(msg);
rs->pending_band_umts = band;
rs->driver->set_band(rs, rs->band_gsm, band,
@@ -681,7 +641,6 @@ static DBusMessage *radio_set_property(DBusConnection *conn, DBusMessage *msg,
if (rs->fast_dormancy_pending == target)
return dbus_message_new_method_return(msg);
rs->pending = dbus_message_ref(msg);
rs->fast_dormancy_pending = target;
rs->driver->set_fast_dormancy(rs, target,
@@ -692,6 +651,26 @@ static DBusMessage *radio_set_property(DBusConnection *conn, DBusMessage *msg,
return __ofono_error_invalid_args(msg);
}
static DBusMessage *radio_get_properties(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ofono_radio_settings *rs = data;
__ofono_dbus_queue_request(rs->q, radio_get_properties_handler,
msg, rs);
return NULL;
}
static DBusMessage *radio_set_property(DBusConnection *conn, DBusMessage *msg,
void *data)
{
struct ofono_radio_settings *rs = data;
__ofono_dbus_queue_request(rs->q, radio_set_property_handler,
msg, rs);
return NULL;
}
static const GDBusMethodTable radio_methods[] = {
{ GDBUS_ASYNC_METHOD("GetProperties",
NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
@@ -730,13 +709,6 @@ void ofono_radio_settings_driver_unregister(const struct ofono_radio_settings_dr
g_drivers = g_slist_remove(g_drivers, (void *) d);
}
static void radio_settings_cancel_get_properties(gpointer data)
{
DBusMessage *msg = data;
__ofono_dbus_pending_reply(&msg, __ofono_error_canceled(msg));
}
static void radio_settings_unregister(struct ofono_atom *atom)
{
struct ofono_radio_settings *rs = __ofono_atom_get_data(atom);
@@ -744,12 +716,7 @@ static void radio_settings_unregister(struct ofono_atom *atom)
DBusConnection *conn = ofono_dbus_get_connection();
struct ofono_modem *modem = __ofono_atom_get_modem(rs->atom);
if (rs->pending_get_prop) {
g_slist_free_full(rs->pending_get_prop,
radio_settings_cancel_get_properties);
rs->pending_get_prop = NULL;
}
__ofono_dbus_queue_free(rs->q);
ofono_modem_remove_interface(modem, OFONO_RADIO_SETTINGS_INTERFACE);
g_dbus_unregister_interface(conn, path, OFONO_RADIO_SETTINGS_INTERFACE);
@@ -793,7 +760,7 @@ struct ofono_radio_settings *ofono_radio_settings_create(struct ofono_modem *mod
return NULL;
rs->mode = -1;
rs->q = __ofono_dbus_queue_new();
rs->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_RADIO_SETTINGS,
radio_settings_remove, rs);

View File

@@ -1581,19 +1581,6 @@ static void sim_imsi_obtained(struct ofono_sim *sim, const char *imsi)
}
static void sim_imsi_cb(const struct ofono_error *error, const char *imsi,
void *data)
{
struct ofono_sim *sim = data;
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
ofono_error("Unable to read IMSI, emergency calls only");
return;
}
sim_imsi_obtained(sim, imsi);
}
static void sim_efimsi_cb(const struct ofono_error *error,
const unsigned char *data, int len, void *user)
{
@@ -1633,6 +1620,26 @@ error:
ofono_error("Unable to read IMSI, emergency calls only");
}
static void sim_imsi_cb(const struct ofono_error *error, const char *imsi,
void *data)
{
struct ofono_sim *sim = data;
if (error->type == OFONO_ERROR_TYPE_NO_ERROR) {
sim_imsi_obtained(sim, imsi);
return;
}
/* Driver function failed, try via EF reads if possible */
if (sim->driver->read_file_transparent == NULL) {
ofono_error("Unable to read IMSI, emergency calls only");
return;
}
sim->driver->read_file_transparent(sim, SIM_EFIMSI_FILEID, 0, 9,
NULL, 0, sim_efimsi_cb, sim);
}
static void sim_retrieve_imsi(struct ofono_sim *sim)
{
if (sim->driver->read_imsi) {
@@ -2608,6 +2615,9 @@ static void sim_query_fac_pinlock_cb(const struct ofono_error *error,
{
struct ofono_sim *sim = data;
if (sim->state == OFONO_SIM_STATE_NOT_PRESENT)
return;
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
goto done;
@@ -2622,6 +2632,9 @@ static void sim_query_fac_networklock_cb(const struct ofono_error *error,
{
struct ofono_sim *sim = data;
if (sim->state == OFONO_SIM_STATE_NOT_PRESENT)
return;
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
goto done;
@@ -2638,6 +2651,9 @@ static void sim_query_fac_imsilock_cb(const struct ofono_error *error,
{
struct ofono_sim *sim = data;
if (sim->state == OFONO_SIM_STATE_NOT_PRESENT)
return;
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
goto done;
@@ -3232,8 +3248,6 @@ void ofono_sim_register(struct ofono_sim *sim)
sim->spn_watches = __ofono_watchlist_new(g_free);
sim->simfs = sim_fs_new(sim, sim->driver);
__ofono_atom_register(sim->atom, sim_unregister);
ofono_sim_add_state_watch(sim, sim_ready, sim, NULL);
if (sim->state > OFONO_SIM_STATE_NOT_PRESENT)
@@ -3242,6 +3256,8 @@ void ofono_sim_register(struct ofono_sim *sim)
sim->hfp_watch = __ofono_modem_add_atom_watch(modem,
OFONO_ATOM_TYPE_EMULATOR_HFP,
emulator_hfp_watch, sim, NULL);
__ofono_atom_register(sim->atom, sim_unregister);
}
void ofono_sim_remove(struct ofono_sim *sim)

View File

@@ -2330,6 +2330,7 @@ char *sms_decode_text(GSList *sms_list)
g_string_append(str, converted);
g_free(converted);
}
g_byte_array_free(utf16, TRUE);
}

View File

@@ -26,6 +26,14 @@ cid = 'CellId'
psc = 'PrimaryScramblingCode'
rssi = 'Strength'
ber = 'BitErrorRate'
rxlev = 'ReceivedSignalStrength'
rscp = 'ReceivedSignalCodePower'
ecn0 = 'ReceivedEnergyRatio'
rsrq = 'ReferenceSignalReceivedQuality'
rsrp = 'ReferenceSignalReceivedPower'
earfcn = 'EARFCN'
eband = 'EBand'
cqi = 'ChannelQualityIndicator'
print("Current serving cell information:")
@@ -50,7 +58,31 @@ if psc in servingcell:
if rssi in servingcell:
print(" [ Signal Strength = %d]" % (servingcell[rssi]))
if rxlev in servingcell:
print(" [ Received Signal Strength = %d]" % (servingcell[rxlev]))
if ber in servingcell:
print(" [ Bit Error Rate = %d]" % (servingcell[ber]))
if rscp in servingcell:
print(" [ Received Signal Code Power = %d]" % (servingcell[rscp]))
if ecn0 in servingcell:
print(" [ Received Energy Ratio = %d]" % (servingcell[ecn0]))
if rsrq in servingcell:
print(" [ Reference Signal Received Quality = %d]" % (servingcell[rsrq]))
if rsrp in servingcell:
print(" [ Reference Signal Received Power = %d]" % (servingcell[rsrp]))
if earfcn in servingcell:
print(" [ E-UTRA Absolue Radio Frequency Channel = %d ]" % (servingcell[earfcn]))
if eband in servingcell:
print(" [ E-UTRA operating Band = %d ]" % (servingcell[eband]))
if cqi in servingcell:
print(" [ Channel Quality Indicator = %d ]" % (servingcell[cqi]))
print('')

26
ofono/test/set-lte-property Executable file
View File

@@ -0,0 +1,26 @@
#!/usr/bin/python3
import dbus
import sys
bus = dbus.SystemBus()
if len(sys.argv) == 4:
path = sys.argv[1]
name = sys.argv[2]
value = sys.argv[3]
elif len(sys.argv) == 3:
manager = dbus.Interface(bus.get_object('org.ofono', '/'),
'org.ofono.Manager')
modems = manager.GetModems()
path = modems[0][0]
name = sys.argv[1]
value = sys.argv[2]
else:
print("%s [PATH] name value" % (sys.argv[0]))
sys.exit(0)
print("Setting {} as {} for modem {}..." .format(name, value, path))
lte = dbus.Interface(bus.get_object('org.ofono', path),
'org.ofono.LongTermEvolution')
lte.SetProperty(name, value)

View File

@@ -29,6 +29,7 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <string.h>
#include <ofono/types.h>

View File

@@ -28,6 +28,7 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <string.h>
#include <ofono/types.h>

View File

@@ -32,6 +32,7 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <string.h>
#include <ofono/modem.h>
#include <ofono/types.h>

View File

@@ -32,6 +32,7 @@
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#include <string.h>
#include <ofono/modem.h>
#include <ofono/types.h>

View File

@@ -83,7 +83,7 @@ struct ofono_netreg {
int ofono_netreg_get_status(struct ofono_netreg *netreg)
{
return netreg ? netreg->status : -1;
return netreg ? (int) netreg->status : -1;
}
const char *ofono_netreg_get_mcc(struct ofono_netreg *netreg)

View File

@@ -1,7 +1,7 @@
Name: ofono
Summary: Open Source Telephony
Version: 1.19
Version: 1.20
Release: 1
Group: Communications/Connectivity Adaptation
License: GPLv2