ofono fails to compile on gcc-6.3
by Pavel Machek
Hi!
I'm getting this:
CC drivers/rilmodem/network-registration.o
drivers/rilmodem/network-registration.c:40:32: error: unknown option
after ‘#pragma GCC diagnostic’ kind [-Werror=pragmas]
#pragma GCC diagnostic ignored "-Wrestrict"
cc1: error: unrecognized command line option ‘-Wno-format-truncation’
[-Werror]
cc1: all warnings being treated as errors
I commented out #pragma, and this allows compilation to
continue... until I hit same issue in
drivers/rilmodem/call-forwarding.c:41:32:
Best regards,
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
2 years, 1 month
Re: Motorola motmdm support
by Pavel Machek
Hi!
> Oh, I think I forgot to answer your question regarding PM,
> there's nothing that the user space needs to except to avoid
> polling the /dev/motmdm* devices unnecessarily. And to type
> AT+SCRN=0 on /dev/motmdm1 when no notifications for signal
> strength and network status are needed.
Is it possible that epoll() does not work properly with /dev/motmdm*?
I am debugging weird problems with ofonod, and that would be an
explanation...
epoll.poll() should be returning list of file descriptors and if they
are ready. And it seems to work for ttyUSB4 but not for motmdm.
Hmm. And motmdm_cdev_poll() lacks EPOLLOUT() support, right? That
could explain things...
Best regards,
Pavel
Type "help", "copyright", "credits" or "license" for more information.
>>> f = open("/dev/ttyUSB4", "r+")
>>> f.write("AT\r\n")
>>>
>>> import select
>>> epoll = select.epoll()
>>> epoll.register(1, select.EPOLLOUT)
>>> epoll.register(f.fileno(), select.EPOLLOUT | select.EPOLLIN)
>>> epoll.poll(1)
[(1, 4), (3, 5)]
>>> select.EPOLLOUT
4
>>> select.EPOLLIN
1
>>> f = open("/dev/motmdm1", "r+")
>>> #f = open("/dev/ttyUSB4", "r+")
... f.write("AT\r\n")
>>>
>>> import select
>>> epoll = select.epoll()
>>> epoll.register(1, select.EPOLLOUT)
>>> epoll.register(f.fileno(), select.EPOLLOUT | select.EPOLLIN)
>>> epoll.poll(1)
[(1, 4)]
>>>
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
2 years, 2 months
Motorola motmdm support
by Pavel Machek
Hi!
Motorola phones use "interesting" setup.
There's qmi and "normal" (but very buggy) AT interface on
them. Unfortunately that uses USB and uses too much power (which is a
problem on phone).
Plus there's /dev/motmdm1, motmdm3 and motmdm9, multiplexed over
serial line.
It uses subset of AT commands (good) with slightly modified
protocol... it says ":OK" instead of "OK" and puts ~ before
unsolicited messages.
Other major difference is that commands need to be sent to the right
device. It seems motmdm1 is for status and call control, and motmdm9
is for incoming sms.
I guess that right way to do this is to introduce
drivers/motorolamodem (or can we just have drivers/motorola?).
Best regards,
Pavel
--
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
2 years, 2 months
Re: Motorola motmdm support
by Denis Kenzior
Hi Tony,
>> How are you handling data connections? PPP? Or is there some other protocol
>> to transfer packets back and forth?
>
> All the data connections in this case are done using QMI over USB.
>
> And that probably explains why I have not seen any data related
> stuff on ts 27.010.
Ew. So is everything related to packet services is running over QMI?
When Pavel explained that he wanted to use the weird squiggly AT
commands, I assumed you wanted to run the packet services over AT as well.
>
>>>> - GNSS/GPS is intended to be handled via oFono LocationReporting API. I
>>>> would recommend integrating this as intended...
>>>
>>> We have a standard kernel GNSS API now :) And we have /dev/gnss0
>>> already working for droid 4.
>>
>> That's great. But I think my recommendation still stands. You want GPS
>> state to be synchronized with the overall modem state, etc...
>
> Hmm sounds like ofono would benefit from using /dev/gnss0 in the
> long run though. I guess the situation is similar to having
> multiple computer mouse drivers earlier with their own interfaces
> vs standard /dev/input :)
Yep, agreed. I already tried to nudge Pavel to propose something.
>
>> I believe the utilities you mention should be good enough to decode the
>> basics. I think someone has even tested these on a real network at some
>> point. But the people working on CDMA have not contributed since ~2011, so
>> I have no idea about the state of that code. I was considering removing it
>> given that CDMA is essentially defunct (or will be in a few years).
>
> OK, sounds like we might need the CDMA PDU features for few more
> years though.
That's fine, as long as someone is actually using these. Right now it
is unmaintained code.
>
> Oh, I think I forgot to answer your question regarding PM,
> there's nothing that the user space needs to except to avoid
> polling the /dev/motmdm* devices unnecessarily. And to type
> AT+SCRN=0 on /dev/motmdm1 when no notifications for signal
> strength and network status are needed.
>
Right, but since /dev/motmdm1 would in theory belong exclusively to
oFono, you probably still need some way of sending the screen 'off'
command. So in effect you're integrating power management with the
telephony stack.
Regards,
-Denis
2 years, 2 months
Re: Motorola motmdm support
by Denis Kenzior
Hi Tony,
On 12/30/2018 02:24 PM, Tony Lindgren wrote:
> * Denis Kenzior <denkenz(a)gmail.com> [181230 19:32]:
>> You still need someone to send AT+CMUX, no? And you most likely need to
>> integrate power management into the telephony stack anyway. As I mentioned,
>> we had a driver like this that worked just fine. oFono ended up using a
>> user space MUX since most of the quality of life improvements to n_gsm came
>> in much later. If I was writing a new MUXed driver these days, I'd
>> seriously consider using GSMIOC_SETCONF and GSMIOC_ENABLE_NET.
>
> No need to type AT+CMUX or anything, things start in ts 27.010
> mode from the start for the hardware. Then the serdev driver
> layer handles doing GSMIOC_SETCONF.
Gotcha. That makes things a bit easier.
>
> I have not played with GSMIOC_ENABLE_NET, I thought that needed
> support on the modem side as well, but maybe not?
>
It requires the modem to support Raw IP. Many (most?) AT command based
modems do these days, no idea about your hardware.
> Would GSMIOC_ENABLE_NET make things easier from ofono
> point of view?
Depends on your definition of easier. The real reason for
GSMIOC_ENABLE_NET is speed/efficiency. In a typical 27.010 / 27.007
compliant modem you'd setup N logical AT command channels where N = 1
(minimum, but can be more if you want) + max number of active contexts.
Typically modems support 3+ of these. Once a context is activated,
you'd switch the channel into data mode via AT+CGDATA (e.g. for PPP or
raw IP) and start shoving your network packets on that channel in the
format chosen.
How are you handling data connections? PPP? Or is there some other
protocol to transfer packets back and forth?
>> - GNSS/GPS is intended to be handled via oFono LocationReporting API. I
>> would recommend integrating this as intended...
>
> We have a standard kernel GNSS API now :) And we have /dev/gnss0
> already working for droid 4.
That's great. But I think my recommendation still stands. You want GPS
state to be synchronized with the overall modem state, etc...
>
>> There's no real functional CDMA support in oFono anyhow. Given that most
>> CDMA networks are about to be switched off, I don't know why you would
>> bother?
>
> Hmm right, good point. Anyways that's how the SMS gets sent and
> received in the US for droid 4 currently.. The file I was looking at
> is src/cdma-sms.c and the only thing to do there is the PDU coding
> and encoding.
I believe the utilities you mention should be good enough to decode the
basics. I think someone has even tested these on a real network at some
point. But the people working on CDMA have not contributed since ~2011,
so I have no idea about the state of that code. I was considering
removing it given that CDMA is essentially defunct (or will be in a few
years).
Regards,
-Denis
2 years, 2 months
Re: Motorola motmdm support
by Denis Kenzior
Hi Tony,
>> It might make your job easier if the oFono driver itself invoked the
>> necessary magic to setup the multiplexer and handed off the devices as
>> needed. We used to have a driver like this, but not sure if it ever made it
>> upstream.
>
> Playing with ldattach and user space handling earlier this year did
> not work out good.. It needed app specific handling for the Motorola
> custom layer on top of ts 27.010, custom handling for all the devices
> such as GNSS and audio mixer, and did not work well with device
> specific power management. So let's not go back to that :)
You still need someone to send AT+CMUX, no? And you most likely need to
integrate power management into the telephony stack anyway. As I
mentioned, we had a driver like this that worked just fine. oFono ended
up using a user space MUX since most of the quality of life improvements
to n_gsm came in much later. If I was writing a new MUXed driver these
days, I'd seriously consider using GSMIOC_SETCONF and GSMIOC_ENABLE_NET.
How does the audio mixer handling work? Just some AT commands to setup
the mixing parameters, or something more involved?
Some other things to consider:
- oFono expects / is designed to expect exclusive access to all the tty
ports
- GNSS/GPS is intended to be handled via oFono LocationReporting API. I
would recommend integrating this as intended...
>
>>> One more question: I guess I'll need to implement this... Is there
>>> another example of driver doing AT commands but on multiple file
>>> descriptors? I could really use something to look at as a template..
>>
>> Any driver for a USB based device would be setup this way. Each AT port is
>> a separate file, e.g. ttyUSB1, ttyACM2, etc. The discovery is done via
>> udev. See plugins/mbm.c or plugins/ublox.c or plugins/telit.c, etc.
>>
>> Assuming you don't want to setup the multiplexer in oFono, then the only
>> tricky part is the port setup. udevng.c setup_serial_modem() assumes a
>> single port, so you might need to add some extra logic to setup the ports
>> via udev rules.
>
> We have network status data at /dev/motmdm1, outgoing SMS PDU device at
> /dev/motmdm3, incoming SMS PDU device at /dev/motmdm9 and so on for each
> ts 27.010 channel. At least incoming and outgoing SMS PDU devices could
> be considered as separate modems if that makes things easier?
No, that just makes things more difficult.
>
>> Alternatively, simply use a config file specific to your driver. See for
>> example how plugins/phonesim.c does this.
>>
>> Or, if it is an extremely platform specific driver, then just hardcode it.
>> E.g. like plugins/calypso.c, which only works for the Freerunner.
>
> Just adding one more thing to consider: Looks like the modem handling for
> SMS PDU's needs to be specific to the nework. First the network needs to
> be detected, and then the GSM or CDMA handlers need to be used for sending
> and receiving SMS. After that things should get standard.. Looks like
> ofono has parsing for the different type SMS PDUs in src/*sms*.c :)
>
There's no real functional CDMA support in oFono anyhow. Given that
most CDMA networks are about to be switched off, I don't know why you
would bother?
Regards,
-Denis
2 years, 2 months
Re: GPIO power management
by Tarmo Kuuse
Hi Martin,
>>> I previously submitted pacthes[1] for the Quectel M95 modem, and now
>>> finally have the time needed to address the review comments.
>>>
>>> In the meantime, my hardware has changed (modem chip remains the same
>>> though), and so I would like to add more generic power management the
>>> ofono driver.
>>>
>>> I would assume that I cannot be the first with the urge to control
>>> modem power using GPIOs (found one example in
>>> ofono/plugins/nokia-gpio.c), so
>>
>> Maybe not the first, but nobody has brought this up for oh ~8 years or
>> so. So might as well treat yourself as the first ;)
>
> I see.. Maybe I will not be the last then :)
You are certainly not the last. I have a Beaglebone-derived device with
a slightly wobbly Quectel UC20 connected to a slightly wobbly USB host
controller. As a preventative measure I plan to toggle reset of both the
USB controller and GSM module before a dial-up session is (re)started.
>>> it might make sense to add GPIO helper in a common object, and use
>>> libgpiod in there?
>>
>> We will not be adding any further dependencies on GLib based libraries
>> for oFono as GLib use is going to be deprecated over time (in favor of
>> ell). This is especially true for a small library like libgpiod.
>
> I don't think libgpiod i GLib based:
>
> ldd /usr/lib/libgpiod.so
> linux-vdso.so.1 (0x00007ffe90977000)
> libc.so.6 => /usr/lib/libc.so.6 (0x00007f325b3d4000)
> /usr/lib64/ld-linux-x86-64.so.2 (0x00007f325b5b4000)
>
>> So this has to be done either inside oFono itself or preferably as a set
>> of class(es) inside ell.
>
> As long as it is only a matter of setting a single GPIO on/off, then I
> think a helper in common/gpio.{c,h} should be enough - something like
> this from my little test program:
>
> static bool gpio_set(uint32_t chip, uint32_t line, uint8_t value)
> {
> struct gpiohandle_request req = {0};
> char path[] = "/dev/gpiochipXX";
> int ret, fd;
>
> snprintf(path, sizeof(path), "/dev/gpiochip%u", chip);
> fd = open(path, O_RDWR | O_CLOEXEC);
> if (fd < 0) {
> error(0, errno, "open: %s", path);
> return false;
> }
>
> req.lineoffsets[0] = line;
> req.lines = 1;
> req.flags = GPIOHANDLE_REQUEST_OUTPUT;
> req.default_values[0] = !!value;
> strncpy(req.consumer_label, "ofono", sizeof(req.consumer_label));
>
> ret = ioctl(fd, GPIO_GET_LINEHANDLE_IOCTL, &req);
> if (ret < 0) {
> error(0, errno, "ioctl");
> return false;
> }
>
> return true;
> }
Do I assume correctly that this is a work in progress? I'm going to hack
out a prototype to test the wobbliness countermeasures, but I'd like to
have a rough idea of how temporary my hack would end up being.
>>> If that would be acceptable, a way to configure such GPIO
>>> numbers/names is needed. Should this be done with an OFONO_CONFIG env
>>> attribute in the udev rules?
>>
>> That is ultimately up to the driver. Serial devices have to rely on
>> udev rules or configuration files, so either should be fine.
>
> udev rules it is then
Are the Ofono udev rules documented anywhere? I can see two .rules files
in the plugins directory, but they're a bit cryptic.
--
Kind regards,
Tarmo
2 years, 2 months
[PATCH] sim: Sim PIN1 cache upon modem reset/crash
by Nandini Rebello
Adding SIM PIN caching feature to oFono. oFono now caches the SIM PIN1 type
against the ICCID throughout its lifetime in a link list and enters
implicitly upon modem reset/crash.
Handles cases of incorrect pin and sim pin changed externally.
Adding to all modems by default.
---
src/sim.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 114 insertions(+)
diff --git a/src/sim.c b/src/sim.c
index 129ff5d..28d9a00 100644
--- a/src/sim.c
+++ b/src/sim.c
@@ -141,6 +141,11 @@ struct ofono_sim {
bool wait_initialized : 1;
};
+struct cached_pin {
+ char *id;
+ char *pin;
+};
+
struct msisdn_set_request {
struct ofono_sim *sim;
int pending;
@@ -176,6 +181,8 @@ static void sim_own_numbers_update(struct ofono_sim *sim);
static GSList *g_drivers = NULL;
+static GSList *cached_pins = NULL;
+
static const char *sim_passwd_name(enum ofono_sim_password_type type)
{
return passwd_name[type];
@@ -473,6 +480,75 @@ done:
return reply;
}
+static struct cached_pin *pin_cache_lookup(const char *iccid)
+{
+ struct cached_pin *c;
+ GSList *l;
+
+ for (l = cached_pins; l; l = l->next) {
+ c = l->data;
+
+ if (g_strcmp0(iccid, c->id) == 0)
+ return c;
+ }
+
+ return NULL;
+}
+
+static gboolean pin_cache_update(const char *iccid, const char *pin)
+{
+ struct cached_pin *pin_cached = pin_cache_lookup(iccid);
+ struct cached_pin *cpins;
+
+ if (pin_cached != NULL) {
+ g_free(pin_cached->pin);
+ pin_cached->pin = g_strdup(pin);
+ return TRUE;
+ }
+
+ cpins = g_new0(struct cached_pin, 1);
+
+ if (cpins == NULL)
+ return FALSE;
+
+ cpins->id = g_strdup(iccid);
+ cpins->pin = g_strdup(pin);
+ cached_pins = g_slist_prepend(cached_pins, cpins);
+
+ return TRUE;
+}
+
+static void pin_cache_remove(const char *iccid)
+{
+ struct cached_pin *pin_cached = pin_cache_lookup(iccid);
+
+ if (pin_cached == NULL)
+ return;
+
+ cached_pins = g_slist_remove(cached_pins, pin_cached);
+}
+
+static void pin_cache_enter_cb(const struct ofono_error *error, void *data)
+{
+ struct ofono_sim *sim = data;
+
+ /* If PIN entry fails, then remove cached PIN*/
+ if (sim->initialized || error->type != OFONO_ERROR_TYPE_NO_ERROR) {
+ pin_cache_remove(sim->iccid);
+ goto recheck;
+ }
+
+ if (sim->pin_type == OFONO_SIM_PASSWORD_SIM_PIN ||
+ sim->pin_type == OFONO_SIM_PASSWORD_SIM_PUK) {
+ sim->wait_initialized = true;
+ DBG("Waiting for ofono_sim_initialized_notify");
+ return;
+ }
+
+recheck:
+ __ofono_sim_recheck_pin(sim);
+}
+
static void sim_pin_retries_query_cb(const struct ofono_error *error,
int retries[OFONO_SIM_PASSWORD_INVALID],
void *data)
@@ -681,6 +757,13 @@ static void sim_locked_cb(struct ofono_sim *sim, gboolean locked)
OFONO_SIM_MANAGER_INTERFACE,
"LockedPins", DBUS_TYPE_STRING,
&locked_pins);
+
+ /*Cache pin only for SIM PIN type*/
+ if (g_strcmp0(typestr, "pin") == 0) {
+ if (!pin_cache_update(sim->iccid, pin))
+ ofono_error("Failed to cache PIN.");
+ }
+
g_strfreev(locked_pins);
sim_pin_retries_check(sim);
@@ -776,6 +859,14 @@ static DBusMessage *sim_unlock_pin(DBusConnection *conn, DBusMessage *msg,
static void sim_change_pin_cb(const struct ofono_error *error, void *data)
{
struct ofono_sim *sim = data;
+ const char *typestr;
+ const char *old;
+ const char *new;
+
+ dbus_message_get_args(sim->pending, NULL, DBUS_TYPE_STRING, &typestr,
+ DBUS_TYPE_STRING, &old,
+ DBUS_TYPE_STRING, &new,
+ DBUS_TYPE_INVALID);
if (error->type != OFONO_ERROR_TYPE_NO_ERROR) {
__ofono_dbus_pending_reply(&sim->pending,
@@ -786,6 +877,12 @@ static void sim_change_pin_cb(const struct ofono_error *error, void *data)
return;
}
+ /*Cache pin only for SIM PIN type*/
+ if (g_strcmp0(typestr, "pin") == 0) {
+ if (!pin_cache_update(sim->iccid, new))
+ ofono_error("Failed to cache PIN.");
+ }
+
__ofono_dbus_pending_reply(&sim->pending,
dbus_message_new_method_return(sim->pending));
@@ -837,8 +934,14 @@ static DBusMessage *sim_change_pin(DBusConnection *conn, DBusMessage *msg,
static void sim_enter_pin_cb(const struct ofono_error *error, void *data)
{
struct ofono_sim *sim = data;
+ const char *typestr;
+ const char *pin;
DBusMessage *reply;
+ dbus_message_get_args(sim->pending, NULL, DBUS_TYPE_STRING, &typestr,
+ DBUS_TYPE_STRING, &pin,
+ DBUS_TYPE_INVALID);
+
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
reply = __ofono_error_failed(sim->pending);
else
@@ -850,6 +953,12 @@ static void sim_enter_pin_cb(const struct ofono_error *error, void *data)
if (sim->initialized || error->type != OFONO_ERROR_TYPE_NO_ERROR)
goto recheck;
+ /*Cache pin only for SIM PIN type*/
+ if (g_strcmp0(typestr, "pin") == 0) {
+ if (!pin_cache_update(sim->iccid, pin))
+ ofono_error("Failed to cache PIN.");
+ }
+
if (sim->pin_type == OFONO_SIM_PASSWORD_SIM_PIN ||
sim->pin_type == OFONO_SIM_PASSWORD_SIM_PUK) {
sim->wait_initialized = true;
@@ -3023,6 +3132,7 @@ static void sim_pin_query_cb(const struct ofono_error *error,
struct ofono_sim *sim = data;
DBusConnection *conn = ofono_dbus_get_connection();
const char *path = __ofono_atom_get_path(sim->atom);
+ struct cached_pin *cpins = pin_cache_lookup(sim->iccid);
const char *pin_name;
char **locked_pins;
gboolean lock_changed;
@@ -3067,6 +3177,10 @@ static void sim_pin_query_cb(const struct ofono_error *error,
&pin_name);
}
+ if (g_strcmp0(pin_name, "pin") == 0 && cpins != NULL)
+ sim->driver->send_passwd(sim, cpins->pin,
+ pin_cache_enter_cb, sim);
+
switch (pin_type) {
case OFONO_SIM_PASSWORD_NONE:
case OFONO_SIM_PASSWORD_SIM_PIN2:
--
2.7.4
2 years, 2 months
[PATCH 2/2] xmm7modem: modified ofono.conf for coex agent
by Antara Borwankar
Added coex agent interface to ofono.conf
---
src/ofono.conf | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/ofono.conf b/src/ofono.conf
index 61e0bde..45cb15c 100644
--- a/src/ofono.conf
+++ b/src/ofono.conf
@@ -16,6 +16,7 @@
<allow send_interface="org.ofono.PositioningRequestAgent"/>
<allow send_interface="org.ofono.HandsfreeAudioAgent"/>
<allow send_interface="org.ofono.NetworkMonitorAgent"/>
+ <allow send_interface="org.ofono.intel.LteCoexistenceAgent"/>
</policy>
<policy at_console="true">
--
1.9.1
2 years, 2 months
[PATCH 1/2] xmm7modem: implemnetation of coexistence functionality
by Antara Borwankar
Added coex implementation in xmm7modem plugin
---
plugins/xmm7xxx.c | 927 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 927 insertions(+)
diff --git a/plugins/xmm7xxx.c b/plugins/xmm7xxx.c
index 195f96b..2f07599 100644
--- a/plugins/xmm7xxx.c
+++ b/plugins/xmm7xxx.c
@@ -57,16 +57,911 @@
#include <drivers/atmodem/atutil.h>
#include <drivers/atmodem/vendor.h>
+#include "ofono.h"
+#include "gdbus.h"
+
+#define OFONO_COEX_INTERFACE OFONO_SERVICE ".intel.LteCoexistence"
+#define OFONO_COEX_AGENT_INTERFACE OFONO_SERVICE ".intel.LteCoexistenceAgent"
+
+#define NET_BAND_LTE_INVALID 0
+#define NET_BAND_LTE_1 101
+#define NET_BAND_LTE_43 143
+#define BAND_LEN 20
+#define MAX_BT_SAFE_VECTOR 15
+#define MAX_WL_SAFE_VECTOR 13
+
static const char *none_prefix[] = { NULL };
static const char *xsimstate_prefix[] = { "+XSIMSTATE:", NULL };
+static const char *xnvmplmn_prefix[] = { "+XNVMPLMN:", NULL };
+
+struct bt_coex_info {
+ int safe_tx_min;
+ int safe_tx_max;
+ int safe_rx_min;
+ int safe_rx_max;
+ int safe_vector[MAX_BT_SAFE_VECTOR];
+ int num_safe_vector;
+};
+
+struct wl_coex_info {
+ int safe_tx_min;
+ int safe_tx_max;
+ int safe_rx_min;
+ int safe_rx_max;
+ int safe_vector[MAX_BT_SAFE_VECTOR];
+ int num_safe_vector;
+};
+
+struct coex_agent {
+ char *path;
+ char *bus;
+ guint disconnect_watch;
+ ofono_bool_t remove_on_terminate;
+ ofono_destroy_func removed_cb;
+ void *removed_data;
+ DBusMessage *msg;
+};
struct xmm7xxx_data {
GAtChat *chat; /* AT chat */
struct ofono_sim *sim;
ofono_bool_t have_sim;
ofono_bool_t sms_phonebook_added;
+ unsigned int netreg_watch;
+};
+
+/* Coex Implementation */
+enum wlan_bw {
+ WLAN_BW_UNSUPPORTED = -1,
+ WLAN_BW_20MHZ = 0,
+ WLAN_BW_40MHZ = 1,
+ WLAN_BW_80MHZ = 2,
+};
+
+struct plmn_hist {
+ unsigned short mnc;
+ unsigned short mcc;
+ unsigned long tdd;
+ unsigned long fdd;
+ unsigned char bw;
+};
+
+struct xmm7xxx_coex {
+ GAtChat *chat;
+ struct ofono_modem *modem;
+
+ DBusMessage *pending;
+ ofono_bool_t bt_active;
+ ofono_bool_t wlan_active;
+ enum wlan_bw wlan_bw;
+ char *lte_band;
+
+ ofono_bool_t pending_bt_active;
+ ofono_bool_t pending_wlan_active;
+ enum wlan_bw pending_wlan_bw;
+
+ struct coex_agent *session_agent;
+};
+
+static ofono_bool_t coex_agent_matches(struct coex_agent *agent,
+ const char *path, const char *sender)
+{
+ return !strcmp(agent->path, path) && !strcmp(agent->bus, sender);
+}
+
+static void coex_agent_set_removed_notify(struct coex_agent *agent,
+ ofono_destroy_func destroy,
+ void *user_data)
+{
+ agent->removed_cb = destroy;
+ agent->removed_data = user_data;
+}
+
+static void coex_agent_send_noreply(struct coex_agent *agent, const char *method)
+{
+ DBusConnection *conn = ofono_dbus_get_connection();
+ DBusMessage *message;
+
+ message = dbus_message_new_method_call(agent->bus, agent->path,
+ OFONO_COEX_INTERFACE,
+ method);
+ if (message == NULL)
+ return;
+
+ dbus_message_set_no_reply(message, TRUE);
+
+ g_dbus_send_message(conn, message);
+}
+
+static void coex_agent_send_release(struct coex_agent *agent)
+{
+ coex_agent_send_noreply(agent, "Release");
+}
+
+static void coex_agent_free(struct coex_agent *agent)
+{
+ DBusConnection *conn = ofono_dbus_get_connection();
+
+ if (agent->disconnect_watch) {
+ coex_agent_send_release(agent);
+
+ g_dbus_remove_watch(conn, agent->disconnect_watch);
+ agent->disconnect_watch = 0;
+ }
+
+ if (agent->removed_cb)
+ agent->removed_cb(agent->removed_data);
+
+ g_free(agent->path);
+ g_free(agent->bus);
+ g_free(agent);
+}
+
+static void coex_agent_disconnect_cb(DBusConnection *conn, void *user_data)
+{
+ struct coex_agent *agent = user_data;
+
+ ofono_debug("Agent exited without calling Unregister");
+
+ agent->disconnect_watch = 0;
+
+ coex_agent_free(agent);
+}
+
+static struct coex_agent *coex_agent_new(const char *path, const char *sender,
+ ofono_bool_t remove_on_terminate)
+{
+ struct coex_agent *agent = g_try_new0(struct coex_agent, 1);
+ DBusConnection *conn = ofono_dbus_get_connection();
+
+ DBG("");
+ if (agent == NULL)
+ return NULL;
+
+ agent->path = g_strdup(path);
+ agent->bus = g_strdup(sender);
+
+ agent->remove_on_terminate = remove_on_terminate;
+
+ agent->disconnect_watch = g_dbus_add_disconnect_watch(conn, sender,
+ coex_agent_disconnect_cb,
+ agent, NULL);
+
+ return agent;
+}
+
+static int coex_agent_coex_wlan_notify(struct coex_agent *agent,
+ const struct wl_coex_info wlan_info)
+{
+ DBusConnection *conn = ofono_dbus_get_connection();
+ DBusMessageIter wl_args, wl_dict, wl_array;
+ const dbus_int32_t *pwl_array = wlan_info.safe_vector;
+ dbus_int32_t value;
+
+ agent->msg = dbus_message_new_method_call(agent->bus, agent->path,
+ OFONO_COEX_AGENT_INTERFACE,
+ "ReceiveWiFiNotification");
+ if (agent->msg == NULL)
+ return -ENOMEM;
+
+ dbus_message_iter_init_append(agent->msg, &wl_args);
+
+ dbus_message_iter_open_container(&wl_args, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_INT32_AS_STRING, &wl_array);
+ dbus_message_iter_append_fixed_array(&wl_array, DBUS_TYPE_INT32,
+ &pwl_array, MAX_WL_SAFE_VECTOR);
+
+ dbus_message_iter_close_container(&wl_args, &wl_array);
+
+ dbus_message_iter_open_container(&wl_args, DBUS_TYPE_ARRAY,
+ "{sv}", &wl_dict);
+
+ value = wlan_info.safe_tx_min;
+ ofono_dbus_dict_append(&wl_dict, "SafeTxMin",
+ DBUS_TYPE_UINT32, &value);
+ value = wlan_info.safe_tx_max;
+ ofono_dbus_dict_append(&wl_dict, "SafeTxMax",
+ DBUS_TYPE_UINT32, &value);
+ value = wlan_info.safe_rx_min;
+ ofono_dbus_dict_append(&wl_dict, "SafeRxMin",
+ DBUS_TYPE_UINT32, &value);
+ value = wlan_info.safe_rx_max;
+ ofono_dbus_dict_append(&wl_dict, "SafeRxMax",
+ DBUS_TYPE_UINT32, &value);
+ value = wlan_info.num_safe_vector;
+ ofono_dbus_dict_append(&wl_dict, "NumSafeVector",
+ DBUS_TYPE_UINT32, &value);
+
+ dbus_message_iter_close_container(&wl_args, &wl_dict);
+ dbus_message_set_no_reply(agent->msg, TRUE);
+
+ if (dbus_connection_send(conn, agent->msg, NULL) == FALSE)
+ return -EIO;
+
+ dbus_message_unref(agent->msg);
+
+ return 0;
+}
+
+static int coex_agent_coex_bt_notify(struct coex_agent *agent,
+ const struct bt_coex_info bt_info)
+{
+ DBusConnection *conn = ofono_dbus_get_connection();
+ DBusMessageIter bt_args, bt_dict, bt_array;
+ const dbus_int32_t *pbt_array = bt_info.safe_vector;
+ int len = MAX_BT_SAFE_VECTOR;
+ dbus_int32_t value;
+
+ agent->msg = dbus_message_new_method_call(agent->bus, agent->path,
+ OFONO_COEX_AGENT_INTERFACE,
+ "ReceiveBTNotification");
+
+ if (agent->msg == NULL)
+ return -ENOMEM;
+
+ pbt_array = bt_info.safe_vector;
+
+ dbus_message_iter_init_append(agent->msg, &bt_args);
+
+ dbus_message_iter_open_container(&bt_args, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_INT32_AS_STRING, &bt_array);
+
+ dbus_message_iter_append_fixed_array(&bt_array, DBUS_TYPE_INT32,
+ &pbt_array, len);
+
+ dbus_message_iter_close_container(&bt_args, &bt_array);
+
+ dbus_message_iter_open_container(&bt_args,
+ DBUS_TYPE_ARRAY, "{sv}", &bt_dict);
+
+ value = bt_info.safe_tx_min;
+ DBG("value = %d", value);
+ ofono_dbus_dict_append(&bt_dict, "SafeTxMin",
+ DBUS_TYPE_UINT32, &value);
+ value = bt_info.safe_tx_max;
+ DBG("value = %d", value);
+ ofono_dbus_dict_append(&bt_dict, "SafeTxMax",
+ DBUS_TYPE_UINT32, &value);
+ value = bt_info.safe_rx_min;
+ DBG("value = %d", value);
+ ofono_dbus_dict_append(&bt_dict, "SafeRxMin",
+ DBUS_TYPE_UINT32, &value);
+ value = bt_info.safe_rx_max;
+ DBG("value = %d", value);
+ ofono_dbus_dict_append(&bt_dict, "SafeRxMax",
+ DBUS_TYPE_UINT32, &value);
+ value = bt_info.num_safe_vector;
+ DBG("value = %d", value);
+ ofono_dbus_dict_append(&bt_dict, "NumSafeVector",
+ DBUS_TYPE_UINT32, &value);
+
+ dbus_message_iter_close_container(&bt_args, &bt_dict);
+
+ if (dbus_connection_send(conn, agent->msg, NULL) == FALSE)
+ return -EIO;
+
+ dbus_message_unref(agent->msg);
+
+ return 0;
+}
+
+static gboolean coex_wlan_bw_from_string(const char *str,
+ enum wlan_bw *band)
+{
+ if (g_str_equal(str, "20")) {
+ *band = WLAN_BW_20MHZ;
+ return TRUE;
+ } else if (g_str_equal(str, "40")) {
+ *band = WLAN_BW_40MHZ;
+ return TRUE;
+ } else if (g_str_equal(str, "80")) {
+ *band = WLAN_BW_80MHZ;
+ return TRUE;
+ } else
+ *band = WLAN_BW_UNSUPPORTED;
+
+ return FALSE;
+}
+
+static const char *wlan_bw_to_string(int band)
+{
+ switch (band) {
+ case WLAN_BW_20MHZ:
+ return "20MHz";
+ case WLAN_BW_40MHZ:
+ return "40MHz";
+ case WLAN_BW_80MHZ:
+ return "80MHz";
+ case WLAN_BW_UNSUPPORTED:
+ return "UnSupported";
+ }
+
+ return "";
+}
+
+static void xmm_get_band_string(int lte_band, char *band)
+{
+ int band_lte;
+
+ band_lte = lte_band-NET_BAND_LTE_1+1;
+
+ if (lte_band >= NET_BAND_LTE_1 && lte_band <= NET_BAND_LTE_43)
+ sprintf(band, "BAND_LTE_%d", band_lte);
+ else
+ sprintf(band, "INVALID");
+}
+
+static DBusMessage *coex_get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct xmm7xxx_coex *coex = data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ dbus_bool_t value;
+ const char *band = NULL;
+
+ 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);
+
+ value = coex->bt_active;
+ ofono_dbus_dict_append(&dict, "BTActive",
+ DBUS_TYPE_BOOLEAN, &value);
+
+ value = coex->wlan_active;
+ ofono_dbus_dict_append(&dict, "WLANActive",
+ DBUS_TYPE_BOOLEAN, &value);
+
+ band = wlan_bw_to_string(coex->wlan_bw);
+ ofono_dbus_dict_append(&dict, "WLANBandwidth",
+ DBUS_TYPE_STRING, &band);
+
+ band = coex->lte_band;
+ ofono_dbus_dict_append(&dict, "Band", DBUS_TYPE_STRING, &band);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+
+static void coex_set_params_cb(gboolean ok, GAtResult *result,
+ gpointer user_data)
+{
+ struct xmm7xxx_coex *coex = user_data;
+ DBusMessage *reply;
+ DBusConnection *conn = ofono_dbus_get_connection();
+ const char *path = ofono_modem_get_path(coex->modem);
+
+ DBG("ok %d", ok);
+
+ if (!ok) {
+ coex->pending_bt_active = coex->bt_active;
+ coex->pending_wlan_active = coex->wlan_active;
+ coex->pending_wlan_bw = coex->wlan_bw;
+ reply = __ofono_error_failed(coex->pending);
+ __ofono_dbus_pending_reply(&coex->pending, reply);
+ return;
+ }
+
+ reply = dbus_message_new_method_return(coex->pending);
+ __ofono_dbus_pending_reply(&coex->pending, reply);
+
+ if (coex->bt_active != coex->pending_bt_active) {
+ coex->bt_active = coex->pending_bt_active;
+ ofono_dbus_signal_property_changed(conn, path,
+ OFONO_COEX_INTERFACE, "BTActive",
+ DBUS_TYPE_BOOLEAN, &coex->bt_active);
+ }
+
+ if (coex->wlan_active != coex->pending_wlan_active) {
+ coex->wlan_active = coex->pending_wlan_active;
+ ofono_dbus_signal_property_changed(conn, path,
+ OFONO_COEX_INTERFACE, "WLANActive",
+ DBUS_TYPE_BOOLEAN, &coex->wlan_active);
+ }
+
+ if (coex->wlan_bw != coex->pending_wlan_bw) {
+ const char *str_band = wlan_bw_to_string(coex->wlan_bw);
+ coex->wlan_bw = coex->pending_wlan_bw;
+
+ ofono_dbus_signal_property_changed(conn, path,
+ OFONO_COEX_INTERFACE, "WLANBandwidth",
+ DBUS_TYPE_STRING, &str_band);
+ }
+}
+
+static void coex_set_params(struct xmm7xxx_coex *coex, ofono_bool_t bt_active,
+ ofono_bool_t wlan_active, int wlan_bw)
+{
+ char buf[64];
+ DBusMessage *reply;
+
+ DBG("");
+ sprintf(buf, "AT+XNRTCWS=65535,%u,%u,%u", (int)wlan_active,
+ wlan_bw, bt_active);
+
+ if (g_at_chat_send(coex->chat, buf, none_prefix,
+ coex_set_params_cb, coex, NULL) > 0)
+ return;
+
+ coex->pending_bt_active = coex->bt_active;
+ coex->pending_wlan_active = coex->wlan_active;
+ coex->pending_wlan_bw = coex->wlan_bw;
+ reply = __ofono_error_failed(coex->pending);
+ __ofono_dbus_pending_reply(&coex->pending, reply);
+}
+
+static DBusMessage *coex_set_property(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct xmm7xxx_coex *coex = data;
+ DBusMessageIter iter;
+ DBusMessageIter var;
+ const char *property;
+ dbus_bool_t value;
+
+ if (coex->pending)
+ return __ofono_error_busy(msg);
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return __ofono_error_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return __ofono_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &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, "BTActive")) {
+ if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
+ return __ofono_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&var, &value);
+
+ if (coex->bt_active == (ofono_bool_t) value)
+ return dbus_message_new_method_return(msg);
+
+ coex->pending_bt_active = value;
+ coex->pending = dbus_message_ref(msg);
+
+ coex_set_params(coex, value, coex->wlan_active, coex->wlan_bw);
+ return NULL;
+ } else if (!strcmp(property, "WLANActive")) {
+ if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
+ return __ofono_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&var, &value);
+
+ if (coex->wlan_active == (ofono_bool_t) value)
+ return dbus_message_new_method_return(msg);
+
+ coex->pending_wlan_active = value;
+ coex->pending = dbus_message_ref(msg);
+
+ coex_set_params(coex, coex->bt_active, value, coex->wlan_bw);
+ return NULL;
+ } else if (g_strcmp0(property, "WLANBandwidth") == 0) {
+ const char *value;
+ enum wlan_bw band;
+
+ if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_STRING)
+ return __ofono_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&var, &value);
+ if (coex_wlan_bw_from_string(value, &band) == FALSE)
+ return __ofono_error_invalid_args(msg);
+
+ if (coex->wlan_bw == band)
+ return dbus_message_new_method_return(msg);
+
+ coex->pending_wlan_bw = band;
+ coex->pending = dbus_message_ref(msg);
+
+ coex_set_params(coex, coex->bt_active, coex->wlan_active, band);
+ return NULL;
+ } else {
+ return __ofono_error_invalid_args(msg);
+ }
+
+ return dbus_message_new_method_return(msg);
+}
+
+static void coex_default_agent_notify(gpointer user_data)
+{
+ struct xmm7xxx_coex *coex = user_data;
+
+ g_at_chat_send(coex->chat, "AT+XNRTCWS=0", none_prefix,
+ NULL, NULL, NULL);
+
+ coex->session_agent = NULL;
+}
+
+static DBusMessage *coex_register_agent(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct xmm7xxx_coex *coex = data;
+ const char *agent_path;
+
+ if (coex->session_agent) {
+ DBG("Coexistence agent already registered");
+ return __ofono_error_busy(msg);
+ }
+
+ if (dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &agent_path,
+ DBUS_TYPE_INVALID) == FALSE)
+ return __ofono_error_invalid_args(msg);
+
+ if (!dbus_validate_path(agent_path, NULL))
+ return __ofono_error_invalid_format(msg);
+
+ coex->session_agent = coex_agent_new(agent_path,
+ dbus_message_get_sender(msg),
+ FALSE);
+
+ if (coex->session_agent == NULL)
+ return __ofono_error_failed(msg);
+
+ coex_agent_set_removed_notify(coex->session_agent,
+ coex_default_agent_notify, coex);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *coex_unregister_agent(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct xmm7xxx_coex *coex = data;
+ const char *agent_path;
+ const char *agent_bus = dbus_message_get_sender(msg);
+
+ if (dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_OBJECT_PATH, &agent_path,
+ DBUS_TYPE_INVALID) == FALSE)
+ return __ofono_error_invalid_args(msg);
+
+ if (coex->session_agent == NULL)
+ return __ofono_error_failed(msg);
+
+ if (!coex_agent_matches(coex->session_agent, agent_path, agent_bus))
+ return __ofono_error_failed(msg);
+
+ coex_agent_send_release(coex->session_agent);
+ coex_agent_free(coex->session_agent);
+
+ g_at_chat_send(coex->chat, "AT+XNRTCWS=0", none_prefix,
+ NULL, NULL, NULL);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static void append_plmn_properties(struct plmn_hist *list,
+ DBusMessageIter *dict)
+{
+ ofono_dbus_dict_append(dict, "MobileCountryCode",
+ DBUS_TYPE_UINT16, &list->mcc);
+ ofono_dbus_dict_append(dict, "MobileNetworkCode",
+ DBUS_TYPE_UINT16, &list->mnc);
+ ofono_dbus_dict_append(dict, "LteBandsFDD",
+ DBUS_TYPE_UINT32, &list->fdd);
+ ofono_dbus_dict_append(dict, "LteBandsTDD",
+ DBUS_TYPE_UINT32, &list->tdd);
+ ofono_dbus_dict_append(dict, "ChannelBandwidth",
+ DBUS_TYPE_UINT32, &list->bw);
+}
+
+static void append_plmn_history_struct_list(struct plmn_hist *list,
+ DBusMessageIter *arr)
+{
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+
+ dbus_message_iter_open_container(arr, DBUS_TYPE_STRUCT, NULL, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ OFONO_PROPERTIES_ARRAY_SIGNATURE,
+ &dict);
+
+ append_plmn_properties(list, &dict);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ dbus_message_iter_close_container(arr, &iter);
+}
+
+static void coex_get_plmn_history_cb(gboolean ok, GAtResult *result,
+ gpointer user_data)
+{
+ struct xmm7xxx_coex *coex = user_data;
+ struct plmn_hist *list = NULL;
+ GAtResultIter iter;
+ int list_size = 0, count;
+ DBusMessage *reply;
+ DBusMessageIter itr, arr;
+ int value;
+
+ DBG("ok %d", ok);
+
+ if (!ok) {
+ __ofono_dbus_pending_reply(&coex->pending,
+ __ofono_error_failed(coex->pending));
+ return;
+ }
+
+ g_at_result_iter_init(&iter, result);
+
+ while (g_at_result_iter_next(&iter, "+XNVMPLMN:")) {
+ if (!list_size)
+ list = g_new0(struct plmn_hist, ++list_size);
+ else
+ list = g_renew(struct plmn_hist, list, ++list_size);
+
+ g_at_result_iter_next_number(&iter, &value);
+ list[list_size - 1].mcc = value;
+ g_at_result_iter_next_number(&iter, &value);
+ list[list_size - 1].mnc = value;
+ g_at_result_iter_next_number(&iter, &value);
+ list[list_size - 1].fdd = value;
+ g_at_result_iter_next_number(&iter, &value);
+ list[list_size - 1].tdd = value;
+ g_at_result_iter_next_number(&iter, &value);
+ list[list_size - 1].bw = value;
+
+ DBG("list_size = %d", list_size);
+ }
+
+ reply = dbus_message_new_method_return(coex->pending);
+ dbus_message_iter_init_append(reply, &itr);
+
+ dbus_message_iter_open_container(&itr, DBUS_TYPE_ARRAY,
+ DBUS_STRUCT_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+ DBUS_STRUCT_END_CHAR_AS_STRING,
+ &arr);
+
+ for (count = 0; count < list_size; count++)
+ append_plmn_history_struct_list(list, &arr);
+
+ dbus_message_iter_close_container(&itr, &arr);
+
+ reply = dbus_message_new_method_return(coex->pending);
+ __ofono_dbus_pending_reply(&coex->pending, reply);
+
+ g_free(list);
+}
+
+static DBusMessage *coex_get_plmn_history(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct xmm7xxx_coex *coex = data;
+
+ if (coex->pending)
+ return __ofono_error_busy(msg);
+
+ if (!g_at_chat_send(coex->chat, "AT+XNVMPLMN=2,2", xnvmplmn_prefix,
+ coex_get_plmn_history_cb, coex, NULL))
+ return __ofono_error_failed(msg);
+
+ coex->pending = dbus_message_ref(msg);
+ return NULL;
+}
+
+static const GDBusMethodTable coex_methods[] = {
+ { GDBUS_METHOD("GetProperties",
+ NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
+ coex_get_properties) },
+ { GDBUS_METHOD("SetProperty",
+ GDBUS_ARGS({ "property", "s" }, { "value", "v" }),
+ NULL, coex_set_property) },
+ { GDBUS_METHOD("RegisterAgent",
+ GDBUS_ARGS({ "path", "o" }), NULL,
+ coex_register_agent) },
+ { GDBUS_METHOD("UnregisterAgent",
+ GDBUS_ARGS({ "path", "o" }), NULL,
+ coex_unregister_agent) },
+ { GDBUS_ASYNC_METHOD("GetPlmnHistory",
+ NULL, GDBUS_ARGS({ "plmnhistory", "a(a{sv})" }),
+ coex_get_plmn_history) },
+ { }
};
+static const GDBusSignalTable coex_signals[] = {
+ { GDBUS_SIGNAL("PropertyChanged",
+ GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
+ { }
+};
+
+static void xmm_coex_w_notify(GAtResult *result, gpointer user_data)
+{
+ struct xmm7xxx_coex *coex = user_data;
+ GAtResultIter iter;
+ int count;
+ struct wl_coex_info wlan;
+
+ g_at_result_iter_init(&iter, result);
+
+ if (!g_at_result_iter_next(&iter, "+XNRTCWSW:"))
+ return;
+
+ g_at_result_iter_next_number(&iter, &wlan.safe_rx_min);
+ g_at_result_iter_skip_next(&iter);
+ g_at_result_iter_skip_next(&iter);
+ g_at_result_iter_next_number(&iter, &wlan.safe_rx_max);
+ g_at_result_iter_skip_next(&iter);
+ g_at_result_iter_skip_next(&iter);
+ g_at_result_iter_next_number(&iter, &wlan.safe_tx_min);
+ g_at_result_iter_skip_next(&iter);
+ g_at_result_iter_skip_next(&iter);
+ g_at_result_iter_next_number(&iter, &wlan.safe_tx_max);
+ g_at_result_iter_skip_next(&iter);
+ g_at_result_iter_skip_next(&iter);
+ g_at_result_iter_skip_next(&iter);
+ g_at_result_iter_next_number(&iter, &wlan.num_safe_vector);
+
+ for (count = 0; count < wlan.num_safe_vector; count++) {
+ g_at_result_iter_next_number(&iter, &wlan.safe_vector[count]);
+ }
+
+ DBG("WLAN notification");
+
+ if (coex->session_agent)
+ coex_agent_coex_wlan_notify(coex->session_agent, wlan);
+}
+
+static void xmm_coex_b_notify(GAtResult *result, gpointer user_data)
+{
+ struct xmm7xxx_coex *coex = user_data;
+ GAtResultIter iter;
+ struct bt_coex_info bt;
+ int count;
+
+ g_at_result_iter_init(&iter, result);
+
+ if (!g_at_result_iter_next(&iter, "+XNRTCWSB:"))
+ return;
+
+ g_at_result_iter_next_number(&iter, &bt.safe_rx_min);
+ g_at_result_iter_next_number(&iter, &bt.safe_rx_max);
+ g_at_result_iter_next_number(&iter, &bt.safe_tx_min);
+ g_at_result_iter_next_number(&iter, &bt.safe_tx_max);
+ g_at_result_iter_next_number(&iter, &bt.num_safe_vector);
+
+ for (count = 0; count < bt.num_safe_vector; count++) {
+ g_at_result_iter_next_number(&iter, &bt.safe_vector[count]);
+ }
+
+ DBG("BT notification");
+
+ if (coex->session_agent)
+ coex_agent_coex_bt_notify(coex->session_agent, bt);
+}
+
+static void xmm_lte_band_notify(GAtResult *result, gpointer user_data)
+{
+ struct xmm7xxx_coex *coex = user_data;
+ GAtResultIter iter;
+ int lte_band;
+ char band[BAND_LEN];
+ const char *path = ofono_modem_get_path(coex->modem);
+ DBusConnection *conn = ofono_dbus_get_connection();
+
+ g_at_result_iter_init(&iter, result);
+
+ if (!g_at_result_iter_next(&iter, "+XCCINFO:"))
+ return;
+
+ g_at_result_iter_skip_next(&iter);
+ g_at_result_iter_skip_next(&iter);
+ g_at_result_iter_skip_next(&iter);
+ g_at_result_iter_skip_next(&iter);
+
+ if (!g_at_result_iter_next_number(&iter, <e_band))
+ return;
+
+ xmm_get_band_string(lte_band, band);
+ DBG("band %s", band);
+
+ if (!strcmp(band, coex->lte_band))
+ return;
+
+ g_free(coex->lte_band);
+ coex->lte_band = g_strdup(band);
+
+ if (coex->lte_band == NULL)
+ return;
+
+ ofono_dbus_signal_property_changed(conn, path,
+ OFONO_COEX_INTERFACE,
+ "Band", DBUS_TYPE_STRING, &coex->lte_band);
+}
+
+static void coex_cleanup(void *data)
+{
+ struct xmm7xxx_coex *coex = data;
+
+ if (coex->pending)
+ __ofono_dbus_pending_reply(&coex->pending,
+ __ofono_error_canceled(coex->pending));
+
+ if (coex->session_agent) {
+ coex_agent_free(coex->session_agent);
+
+ g_at_chat_send(coex->chat, "AT+XNRTCWS=0", none_prefix,
+ NULL, NULL, NULL);
+ }
+
+ g_free(coex->lte_band);
+ g_free(coex);
+}
+
+static int xmm_coex_enable(struct ofono_modem *modem, void *data)
+{
+ struct xmm7xxx_coex *coex;
+ DBusConnection *conn = ofono_dbus_get_connection();
+ const char *path = ofono_modem_get_path(modem);
+ coex = g_new0(struct xmm7xxx_coex, 1);
+
+ DBG("coex enable");
+
+ coex->chat = data;
+ coex->modem = modem;
+ coex->bt_active = 0;
+ coex->wlan_active = 0;
+ coex->wlan_bw = WLAN_BW_20MHZ;
+ coex->lte_band = g_strdup("INVALID");
+ coex->session_agent = NULL;
+
+ if (!g_at_chat_send(coex->chat, "AT+XCCINFO=1", none_prefix,
+ NULL, NULL, NULL))
+ goto out;
+
+ if (!g_at_chat_send(coex->chat, "AT+XNRTCWS=7", none_prefix,
+ NULL, NULL, NULL))
+ goto out;
+
+ if (!g_dbus_register_interface(conn, path, OFONO_COEX_INTERFACE,
+ coex_methods,
+ coex_signals,
+ NULL, coex, coex_cleanup)) {
+ ofono_error("Could not register %s interface under %s",
+ OFONO_COEX_INTERFACE, path);
+ goto out;
+ }
+
+ ofono_modem_add_interface(modem, OFONO_COEX_INTERFACE);
+
+ g_at_chat_register(coex->chat, "+XNRTCWSW:", xmm_coex_w_notify,
+ FALSE, coex, NULL);
+ g_at_chat_register(coex->chat, "+XNRTCWSB:", xmm_coex_b_notify,
+ FALSE, coex, NULL);
+ g_at_chat_register(coex->chat, "+XCCINFO:", xmm_lte_band_notify,
+ FALSE, coex, NULL);
+ return 0;
+
+out:
+ g_free(coex->lte_band);
+ g_free(coex);
+ return -EIO;
+}
+
+/* Coex Implementation Ends*/
+
static void xmm7xxx_debug(const char *str, void *user_data)
{
const char *prefix = user_data;
@@ -242,6 +1137,29 @@ static void cfun_enable_cb(gboolean ok, GAtResult *result, gpointer user_data)
xsimstate_query_cb, modem, NULL);
}
+static void netreg_watch(struct ofono_atom *atom,
+ enum ofono_atom_watch_condition cond,
+ void *data)
+{
+ struct ofono_modem *modem = data;
+ struct xmm7xxx_data *modem_data = ofono_modem_get_data(modem);
+ DBusConnection *conn = ofono_dbus_get_connection();
+ const char *path = ofono_modem_get_path(modem);
+
+ if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
+ if (g_dbus_unregister_interface(conn, path,
+ OFONO_COEX_INTERFACE))
+ ofono_modem_remove_interface(modem,
+ OFONO_COEX_INTERFACE);
+ return;
+ }
+
+ if (cond == OFONO_ATOM_WATCH_CONDITION_REGISTERED) {
+ xmm_coex_enable(modem, modem_data->chat);
+ return;
+ }
+}
+
static int xmm7xxx_enable(struct ofono_modem *modem)
{
struct xmm7xxx_data *data = ofono_modem_get_data(modem);
@@ -263,6 +1181,10 @@ static int xmm7xxx_enable(struct ofono_modem *modem)
g_at_chat_send(data->chat, "AT+CFUN=4", none_prefix,
cfun_enable_cb, modem, NULL);
+ data->netreg_watch = __ofono_modem_add_atom_watch(modem,
+ OFONO_ATOM_TYPE_NETREG,
+ netreg_watch, modem, NULL);
+
return -EINPROGRESS;
}
@@ -293,6 +1215,11 @@ static int xmm7xxx_disable(struct ofono_modem *modem)
g_at_chat_send(data->chat, "AT+CFUN=0", none_prefix,
cfun_disable_cb, modem, NULL);
+ if (data->netreg_watch) {
+ __ofono_modem_remove_atom_watch(modem, data->netreg_watch);
+ data->netreg_watch = 0;
+ }
+
return -EINPROGRESS;
}
--
1.9.1
2 years, 2 months