mirror of
https://git.kernel.org/pub/scm/network/wireless/iwd.git
synced 2024-11-25 17:59:25 +01:00
789 lines
18 KiB
C
789 lines
18 KiB
C
/*
|
|
*
|
|
* Wireless daemon for Linux
|
|
*
|
|
* Copyright (C) 2017 Intel Corporation. All rights reserved.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include <stdio.h>
|
|
#include <errno.h>
|
|
|
|
#include <ell/ell.h>
|
|
#include <ell/plugin.h>
|
|
#include <ell/dbus.h>
|
|
|
|
#include "src/dbus.h"
|
|
#include "src/simauth.h"
|
|
#include "src/backtrace.h"
|
|
|
|
/*
|
|
* This plugin takes care of all the communication with ofono in order to
|
|
* provide the needed algorithms for EAP-SIM/AKA/AKA'. Once this plugin is
|
|
* started it will start the initial "discovery" stage i.e.
|
|
*
|
|
* 1. Find ofono DBus service
|
|
* 2. Find all modems (and listen for Added/Removed signal)
|
|
* 3. For each modem see if SimAuthentication interface exists and get apps
|
|
* and NAI
|
|
* 4. Create simauth provider for modem if the above succeeds
|
|
*
|
|
* These steps are chained, as to avoid the complexity of concurrent method
|
|
* calls. Once the chain of methods has completed, a new simauth provider is
|
|
* created for the modem. Only then will the EAP methods be able to run the
|
|
* authentication algorithms ofono provides.
|
|
*
|
|
* If at any time the above conditions change e.g. SimAuthentication disappears,
|
|
* ofono disappears, the modem simauth provider will unregister itself from
|
|
* simauth, not allowing any future authentication algorithms to be run until
|
|
* the start conditions are met again.
|
|
*/
|
|
|
|
#define OFONO_SIM_AUTHENTICATION_IFACE "org.ofono.SimAuthentication"
|
|
#define OFONO_SIM_MANAGER_IFACE "org.ofono.SimManager"
|
|
#define OFONO_MODEM_IFACE "org.ofono.Modem"
|
|
#define OFONO_USIM_APPLICATION_IFACE "org.ofono.USimApplication"
|
|
#define OFONO_ISIM_APPLICATION_IFACE "org.ofono.ISimApplication"
|
|
|
|
struct sa_data {
|
|
char *umts_app_path;
|
|
char *ims_app_path;
|
|
|
|
struct user_cb *pending;
|
|
uint32_t serial;
|
|
};
|
|
|
|
struct ofono_modem {
|
|
char *path;
|
|
int props_watch;
|
|
|
|
uint32_t props_serial;
|
|
uint32_t apps_serial;
|
|
|
|
bool sim_auth_found : 1;
|
|
|
|
struct iwd_sim_auth *auth;
|
|
};
|
|
|
|
struct user_cb {
|
|
void *cb;
|
|
void *data;
|
|
bool is_gsm : 1;
|
|
};
|
|
|
|
static uint32_t ofono_watch;
|
|
static uint32_t modem_add_watch;
|
|
static uint32_t modem_removed_watch;
|
|
struct l_queue *modems;
|
|
|
|
static struct user_cb *new_cb(void *func, void *data, bool is_gsm)
|
|
{
|
|
struct user_cb *cbd = l_new(struct user_cb, 1);
|
|
|
|
cbd->cb = func;
|
|
cbd->data = data;
|
|
cbd->is_gsm = is_gsm;
|
|
|
|
return cbd;
|
|
}
|
|
|
|
static void free_cb(void *ptr)
|
|
{
|
|
struct sa_data *sa_data = ptr;
|
|
|
|
l_free(sa_data->pending);
|
|
sa_data->pending = NULL;
|
|
sa_data->serial = 0;
|
|
}
|
|
|
|
/*
|
|
* Copy a byte array ("ay") from array into buf
|
|
*/
|
|
static bool get_byte_array(struct l_dbus_message_iter *array, uint8_t *buf,
|
|
int len)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < len; i++) {
|
|
if (!l_dbus_message_iter_next_entry(array, buf + i))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* Append a byte array ("ay") to a DBus message builder
|
|
*/
|
|
static bool append_byte_array(struct l_dbus_message_builder *builder,
|
|
const uint8_t *data, int len)
|
|
{
|
|
int i;
|
|
|
|
if (!l_dbus_message_builder_enter_array(builder, "y"))
|
|
return false;
|
|
|
|
for (i = 0; i < len; i++)
|
|
if (!l_dbus_message_builder_append_basic(builder, 'y',
|
|
data + i))
|
|
return false;
|
|
|
|
if (!l_dbus_message_builder_leave_array(builder))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void ims_auth_cb(struct l_dbus_message *reply, void *user_data)
|
|
{
|
|
struct sa_data *sa_data = user_data;
|
|
struct user_cb *cbd = sa_data->pending;
|
|
sim_auth_check_milenage_cb_t cb = cbd->cb;
|
|
struct l_dbus_message_iter properties;
|
|
struct l_dbus_message_iter value;
|
|
const char *prop;
|
|
uint8_t res[8];
|
|
uint8_t ck[16];
|
|
uint8_t ik[16];
|
|
uint8_t auts[16];
|
|
|
|
if (l_dbus_message_is_error(reply)) {
|
|
l_debug("ImsAuthenticate error");
|
|
goto end;
|
|
}
|
|
|
|
if (!l_dbus_message_get_arguments(reply, "a{say}", &properties))
|
|
goto end;
|
|
|
|
while (l_dbus_message_iter_next_entry(&properties, &prop, &value)) {
|
|
if (!strcmp(prop, "RES")) {
|
|
if (!get_byte_array(&value, res, 8))
|
|
goto end;
|
|
} else if (!strcmp(prop, "CK")) {
|
|
if (!get_byte_array(&value, ck, 16))
|
|
goto end;
|
|
} else if (!strcmp(prop, "IK")) {
|
|
if (!get_byte_array(&value, ik, 16))
|
|
goto end;
|
|
} else if (!strcmp(prop, "AUTS")) {
|
|
if (!get_byte_array(&value, auts, 14))
|
|
goto end;
|
|
|
|
cb(NULL, NULL, NULL, auts, cbd->data);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
cb(res, ck, ik, NULL, cbd->data);
|
|
|
|
return;
|
|
|
|
end:
|
|
cb(NULL, NULL, NULL, NULL, cbd->data);
|
|
}
|
|
|
|
static void gsm_auth_cb(struct l_dbus_message *reply, void *user_data)
|
|
{
|
|
struct sa_data *sa_data = user_data;
|
|
struct user_cb *cbd = sa_data->pending;
|
|
sim_auth_run_gsm_cb_t cb = cbd->cb;
|
|
struct l_dbus_message_iter array;
|
|
struct l_dbus_message_iter val;
|
|
struct l_dbus_message_iter dict;
|
|
const char *prop;
|
|
int sres_pos = 0;
|
|
int kc_pos = 0;
|
|
uint8_t kc[NUM_RANDS_MAX][EAP_SIM_KC_LEN];
|
|
uint8_t sres[NUM_RANDS_MAX][EAP_SIM_SRES_LEN];
|
|
|
|
if (l_dbus_message_is_error(reply)) {
|
|
l_debug("GsmAuthenticate error");
|
|
goto end;
|
|
}
|
|
|
|
if (!l_dbus_message_get_arguments(reply, "aa{say}", &array))
|
|
goto end;
|
|
|
|
while (l_dbus_message_iter_next_entry(&array, &dict)) {
|
|
while (l_dbus_message_iter_next_entry(&dict, &prop, &val)) {
|
|
if (sres_pos > NUM_RANDS_MAX || kc_pos > NUM_RANDS_MAX)
|
|
goto end;
|
|
|
|
if (!strcmp(prop, "SRES")) {
|
|
if (!get_byte_array(&val, sres[sres_pos++],
|
|
EAP_SIM_SRES_LEN))
|
|
goto end;
|
|
} else if (!strcmp(prop, "Kc")) {
|
|
if (!get_byte_array(&val, kc[kc_pos++],
|
|
EAP_SIM_KC_LEN))
|
|
goto end;
|
|
}
|
|
}
|
|
}
|
|
|
|
cb((const uint8_t *)sres, (const uint8_t *)kc, cbd->data);
|
|
|
|
return;
|
|
|
|
end:
|
|
cb(NULL, NULL, cbd->data);
|
|
}
|
|
|
|
static int ofono_sim_auth_run_gsm(struct iwd_sim_auth *auth,
|
|
const uint8_t *rands, int num_rands, sim_auth_run_gsm_cb_t cb,
|
|
void *data)
|
|
{
|
|
struct sa_data *sa_data = iwd_sim_auth_get_data(auth);
|
|
struct l_dbus *dbus = dbus_get_bus();
|
|
struct l_dbus_message *message;
|
|
struct l_dbus_message_builder *builder;
|
|
int i;
|
|
|
|
if (num_rands > NUM_RANDS_MAX) {
|
|
l_debug("Max number of RAND's is %d", NUM_RANDS_MAX);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (sa_data->pending) {
|
|
l_debug("Modem already has outstanding auth request");
|
|
return -EBUSY;
|
|
}
|
|
|
|
sa_data->pending = new_cb(cb, data, true);
|
|
|
|
message = l_dbus_message_new_method_call(dbus, "org.ofono",
|
|
sa_data->umts_app_path, OFONO_USIM_APPLICATION_IFACE,
|
|
"GsmAuthenticate");
|
|
|
|
builder = l_dbus_message_builder_new(message);
|
|
|
|
if (!l_dbus_message_builder_enter_array(builder, "ay"))
|
|
goto error;
|
|
|
|
for (i = 0; i < num_rands; i++) {
|
|
if (!append_byte_array(builder, rands + (i * 16), 16))
|
|
goto error;
|
|
}
|
|
|
|
if (!l_dbus_message_builder_leave_array(builder))
|
|
goto error;
|
|
|
|
if (!l_dbus_message_builder_finalize(builder))
|
|
goto error;
|
|
|
|
sa_data->serial = l_dbus_send_with_reply(dbus, message, gsm_auth_cb,
|
|
sa_data, free_cb);
|
|
if (!sa_data->serial)
|
|
goto error;
|
|
|
|
l_dbus_message_builder_destroy(builder);
|
|
|
|
return 0;
|
|
|
|
error:
|
|
l_dbus_message_builder_destroy(builder);
|
|
l_free(sa_data->pending);
|
|
sa_data->pending = NULL;
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
static int ofono_sim_auth_check_milenage(struct iwd_sim_auth *auth,
|
|
const uint8_t *rand, const uint8_t *autn,
|
|
sim_auth_check_milenage_cb_t cb, void *data)
|
|
{
|
|
struct sa_data *sa_data = iwd_sim_auth_get_data(auth);
|
|
const char *iface = OFONO_ISIM_APPLICATION_IFACE;
|
|
const char *method = "ImsAuthenticate";
|
|
const char *path;
|
|
struct l_dbus *dbus = dbus_get_bus();
|
|
struct l_dbus_message *message;
|
|
struct l_dbus_message_builder *builder;
|
|
|
|
if (sa_data->pending) {
|
|
l_debug("Modem already has outstanding auth request");
|
|
return -EBUSY;
|
|
}
|
|
|
|
sa_data->pending = new_cb(cb, data, false);
|
|
|
|
/*
|
|
* If ISIM is not available, run on USIM application
|
|
*/
|
|
if (!sa_data->ims_app_path && sa_data->umts_app_path) {
|
|
iface = OFONO_USIM_APPLICATION_IFACE;
|
|
method = "UmtsAuthenticate";
|
|
path = sa_data->umts_app_path;
|
|
} else {
|
|
path = sa_data->ims_app_path;
|
|
}
|
|
|
|
message = l_dbus_message_new_method_call(dbus, "org.ofono", path,
|
|
iface, method);
|
|
|
|
builder = l_dbus_message_builder_new(message);
|
|
|
|
if (!append_byte_array(builder, rand, 16))
|
|
goto error;
|
|
|
|
if (!append_byte_array(builder, autn, 16))
|
|
goto error;
|
|
|
|
if (!l_dbus_message_builder_finalize(builder))
|
|
goto error;
|
|
|
|
sa_data->serial = l_dbus_send_with_reply(dbus, message, ims_auth_cb,
|
|
sa_data, free_cb);
|
|
if (!sa_data->serial)
|
|
goto error;
|
|
|
|
l_dbus_message_builder_destroy(builder);
|
|
|
|
return 0;
|
|
|
|
error:
|
|
l_dbus_message_builder_destroy(builder);
|
|
l_free(sa_data->pending);
|
|
sa_data->pending = NULL;
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
static void ofono_sim_auth_cancel_request(struct iwd_sim_auth *auth, int id)
|
|
{
|
|
struct sa_data *sa_data = iwd_sim_auth_get_data(auth);
|
|
|
|
if (sa_data->pending) {
|
|
l_dbus_cancel(dbus_get_bus(), sa_data->serial);
|
|
sa_data->pending = NULL;
|
|
}
|
|
}
|
|
|
|
static void ofono_sim_auth_remove(struct iwd_sim_auth *auth)
|
|
{
|
|
struct sa_data *sa_data = iwd_sim_auth_get_data(auth);
|
|
|
|
l_debug("removing auth data %p", sa_data);
|
|
|
|
if (sa_data->pending) {
|
|
struct user_cb *cbd = sa_data->pending;
|
|
|
|
if (cbd->is_gsm) {
|
|
sim_auth_run_gsm_cb_t cb = cbd->cb;
|
|
|
|
cb(NULL, NULL, cbd->data);
|
|
} else {
|
|
sim_auth_check_milenage_cb_t cb = cbd->cb;
|
|
|
|
cb(NULL, NULL, NULL, NULL, cbd->data);
|
|
}
|
|
|
|
l_dbus_cancel(dbus_get_bus(), sa_data->serial);
|
|
}
|
|
|
|
l_free(sa_data->ims_app_path);
|
|
l_free(sa_data->umts_app_path);
|
|
l_free(sa_data);
|
|
}
|
|
|
|
static struct iwd_sim_auth_driver ofono_driver = {
|
|
.name = "oFono SimAuth driver",
|
|
.check_milenage = ofono_sim_auth_check_milenage,
|
|
.run_gsm = ofono_sim_auth_run_gsm,
|
|
.cancel_request = ofono_sim_auth_cancel_request,
|
|
.remove = ofono_sim_auth_remove
|
|
};
|
|
|
|
static void modem_destroy(void *data)
|
|
{
|
|
struct l_dbus *dbus = dbus_get_bus();
|
|
struct ofono_modem *modem = data;
|
|
|
|
if (modem->auth) {
|
|
/*
|
|
* If an auth instance has been created, simauth will call
|
|
* the driver's remove which cleanups the sa_data object
|
|
*/
|
|
iwd_sim_auth_remove(modem->auth);
|
|
}
|
|
|
|
l_debug("removing modem %s\n", modem->path);
|
|
|
|
if (modem->apps_serial)
|
|
l_dbus_cancel(dbus, modem->apps_serial);
|
|
|
|
if (modem->props_serial)
|
|
l_dbus_cancel(dbus, modem->props_serial);
|
|
|
|
l_free(modem->path);
|
|
l_dbus_remove_watch(dbus, modem->props_watch);
|
|
l_free(modem);
|
|
}
|
|
|
|
static void get_auth_apps_cb(struct l_dbus_message *reply,
|
|
void *user_data)
|
|
{
|
|
struct ofono_modem *modem = user_data;
|
|
struct sa_data *sa_data = iwd_sim_auth_get_data(modem->auth);
|
|
struct l_dbus_message_iter array;
|
|
struct l_dbus_message_iter dict;
|
|
struct l_dbus_message_iter variant;
|
|
bool sim_supported = false;
|
|
bool aka_supported = false;
|
|
|
|
const char *path;
|
|
|
|
modem->apps_serial = 0;
|
|
|
|
if (l_dbus_message_is_error(reply)) {
|
|
l_debug("GetApplications error");
|
|
goto error;
|
|
}
|
|
|
|
if (!l_dbus_message_get_arguments(reply, "a{oa{sv}}", &array))
|
|
goto error;
|
|
|
|
while (l_dbus_message_iter_next_entry(&array, &path, &dict)) {
|
|
const char *type;
|
|
const char *label;
|
|
const char *key;
|
|
|
|
while (l_dbus_message_iter_next_entry(&dict, &key, &variant)) {
|
|
if (strcmp(key, "Type"))
|
|
continue;
|
|
|
|
if (!l_dbus_message_iter_get_variant(&variant, "s",
|
|
&type, &label))
|
|
goto error;
|
|
|
|
if (!strcmp(type, "Umts")) {
|
|
sa_data->umts_app_path = l_strdup(path);
|
|
sim_supported = true;
|
|
aka_supported = true;
|
|
} else if (!strcmp(type, "Ims")) {
|
|
sa_data->ims_app_path = l_strdup(path);
|
|
aka_supported = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (sa_data->umts_app_path || sa_data->ims_app_path) {
|
|
iwd_sim_auth_set_capabilities(modem->auth, sim_supported,
|
|
aka_supported);
|
|
|
|
iwd_sim_auth_register(modem->auth);
|
|
|
|
l_debug("modem %s successfully loaded, sim=%u, aka=%u",
|
|
modem->path, sim_supported, aka_supported);
|
|
return;
|
|
}
|
|
|
|
/* non supported type */
|
|
l_debug("unsupported modem auth capabilities");
|
|
|
|
error:
|
|
iwd_sim_auth_remove(modem->auth);
|
|
}
|
|
|
|
static void get_applications(struct ofono_modem *modem)
|
|
{
|
|
struct l_dbus *dbus = dbus_get_bus();
|
|
struct l_dbus_message *message;
|
|
|
|
message = l_dbus_message_new_method_call(dbus,
|
|
"org.ofono", modem->path,
|
|
OFONO_SIM_AUTHENTICATION_IFACE,
|
|
"GetApplications");
|
|
|
|
l_dbus_message_set_arguments(message, "");
|
|
|
|
modem->apps_serial = l_dbus_send_with_reply(dbus, message,
|
|
get_auth_apps_cb, modem, NULL);
|
|
}
|
|
|
|
static void get_auth_props_cb(struct l_dbus_message *reply,
|
|
void *user_data)
|
|
{
|
|
struct ofono_modem *modem = user_data;
|
|
struct l_dbus_message_iter array;
|
|
struct l_dbus_message_iter variant;
|
|
const char *key;
|
|
|
|
modem->props_serial = 0;
|
|
|
|
if (l_dbus_message_is_error(reply)) {
|
|
l_debug("GetProperties error");
|
|
goto error;
|
|
}
|
|
|
|
if (!l_dbus_message_get_arguments(reply, "a{sv}", &array))
|
|
goto error;
|
|
|
|
while (l_dbus_message_iter_next_entry(&array, &key, &variant)) {
|
|
if (!strcmp(key, "NetworkAccessIdentity")) {
|
|
struct sa_data *sa_data;
|
|
const char *id;
|
|
|
|
if (!l_dbus_message_iter_get_variant(&variant,
|
|
"s", &id))
|
|
goto error;
|
|
|
|
modem->auth = iwd_sim_auth_create(&ofono_driver);
|
|
|
|
sa_data = l_new(struct sa_data, 1);
|
|
|
|
iwd_sim_auth_set_data(modem->auth, sa_data);
|
|
iwd_sim_auth_set_nai(modem->auth, id);
|
|
|
|
get_applications(modem);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
error:
|
|
if (modem->auth)
|
|
iwd_sim_auth_remove(modem->auth);
|
|
}
|
|
|
|
static void get_properties(struct ofono_modem *modem)
|
|
{
|
|
struct l_dbus *dbus = dbus_get_bus();
|
|
struct l_dbus_message *message;
|
|
|
|
message = l_dbus_message_new_method_call(dbus,
|
|
"org.ofono", modem->path,
|
|
OFONO_SIM_AUTHENTICATION_IFACE,
|
|
"GetProperties");
|
|
|
|
l_dbus_message_set_arguments(message, "");
|
|
|
|
modem->props_serial = l_dbus_send_with_reply(dbus, message,
|
|
get_auth_props_cb, modem, NULL);
|
|
}
|
|
|
|
static void parse_interfaces(struct l_dbus_message_iter *prop,
|
|
struct ofono_modem *modem)
|
|
{
|
|
struct l_dbus_message_iter ifaces;
|
|
const char *str;
|
|
|
|
if (!l_dbus_message_iter_get_variant(prop, "as", &ifaces)) {
|
|
l_warn("error parsing modem %s interfaces", modem->path);
|
|
return;
|
|
}
|
|
|
|
while (l_dbus_message_iter_next_entry(&ifaces, &str)) {
|
|
if (!strcmp(OFONO_SIM_AUTHENTICATION_IFACE, str)) {
|
|
if (modem->sim_auth_found)
|
|
return;
|
|
|
|
modem->sim_auth_found = true;
|
|
|
|
get_properties(modem);
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* SimAuthentication disappeared */
|
|
if (modem->sim_auth_found) {
|
|
/* Remove auth provider, this will free the sa_data object */
|
|
if (modem->auth)
|
|
iwd_sim_auth_remove(modem->auth);
|
|
|
|
/* put modem back into a 'discovery' state */
|
|
modem->sim_auth_found = false;
|
|
}
|
|
}
|
|
|
|
static void interfaces_changed_cb(struct l_dbus_message *message,
|
|
void *user_data)
|
|
{
|
|
struct ofono_modem *modem = user_data;
|
|
struct l_dbus_message_iter value;
|
|
const char *key;
|
|
|
|
l_dbus_message_get_arguments(message, "sv", &key, &value);
|
|
|
|
if (!strcmp(key, "Interfaces"))
|
|
parse_interfaces(&value, modem);
|
|
}
|
|
|
|
static bool match_modem_by_path(const void *a, const void *b)
|
|
{
|
|
struct ofono_modem *modem = (struct ofono_modem *)a;
|
|
|
|
if (strcmp(modem->path, (const char *)b))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void parse_modem(const char *path, struct l_dbus_message_iter *props)
|
|
{
|
|
struct l_dbus_message_iter value;
|
|
const char *key;
|
|
struct ofono_modem *modem;
|
|
|
|
l_debug("modem found: %s", path);
|
|
|
|
if (l_queue_find(modems, match_modem_by_path, path)) {
|
|
/*
|
|
* TODO: This can be removed once its found why sometimes two
|
|
* modem added callbacks happen. It is very infrequent but
|
|
* has been seen to happen.
|
|
*/
|
|
l_error("modem %s already found", path);
|
|
#ifdef HAVE_EXECINFO_H
|
|
__iwd_backtrace_print(2);
|
|
#endif
|
|
exit(1);
|
|
}
|
|
|
|
modem = l_new(struct ofono_modem, 1);
|
|
modem->path = l_strdup(path);
|
|
|
|
while (l_dbus_message_iter_next_entry(props, &key, &value)) {
|
|
if (!strcmp(key, "Interfaces"))
|
|
parse_interfaces(&value, modem);
|
|
}
|
|
|
|
/*
|
|
* add watch in case SimAuthentication goes away
|
|
*/
|
|
modem->props_watch = l_dbus_add_signal_watch(dbus_get_bus(),
|
|
"org.ofono", path, OFONO_MODEM_IFACE,
|
|
"PropertyChanged", L_DBUS_MATCH_ARGUMENT(0),
|
|
"Interfaces", L_DBUS_MATCH_NONE,
|
|
interfaces_changed_cb, modem);
|
|
|
|
l_queue_push_tail(modems, modem);
|
|
}
|
|
|
|
static void modem_added_cb(struct l_dbus_message *message, void *user_data)
|
|
{
|
|
struct l_dbus_message_iter props;
|
|
const char *path;
|
|
|
|
l_debug("");
|
|
|
|
if (!l_dbus_message_get_arguments(message, "oa{sv}", &path, &props))
|
|
return;
|
|
|
|
parse_modem(path, &props);
|
|
}
|
|
|
|
static void modem_removed_cb(struct l_dbus_message *message, void *user_data)
|
|
{
|
|
const char *path;
|
|
struct ofono_modem *modem;
|
|
|
|
if (!l_dbus_message_get_arguments(message, "o", &path))
|
|
return;
|
|
|
|
modem = l_queue_remove_if(modems, match_modem_by_path, path);
|
|
if (!modem) {
|
|
l_warn("Cannot remove modem %s, not found", path);
|
|
return;
|
|
}
|
|
|
|
modem_destroy(modem);
|
|
}
|
|
|
|
static void get_modems_cb(struct l_dbus_message *reply, void *user_data)
|
|
{
|
|
struct l_dbus_message_iter props;
|
|
struct l_dbus_message_iter modem_list;
|
|
const char *path = NULL;
|
|
|
|
if (l_dbus_message_is_error(reply)) {
|
|
l_debug("Error discovering modems");
|
|
return;
|
|
}
|
|
|
|
modems = l_queue_new();
|
|
|
|
l_dbus_message_get_arguments(reply, "a(oa{sv})", &modem_list);
|
|
|
|
while (l_dbus_message_iter_next_entry(&modem_list, &path, &props))
|
|
parse_modem(path, &props);
|
|
|
|
/* watch for modems being added/removed */
|
|
modem_add_watch = l_dbus_add_signal_watch(dbus_get_bus(),
|
|
"org.ofono", "/", "org.ofono.Manager", "ModemAdded",
|
|
L_DBUS_MATCH_NONE, modem_added_cb, NULL);
|
|
|
|
modem_removed_watch = l_dbus_add_signal_watch(dbus_get_bus(),
|
|
"org.ofono", "/", "org.ofono.Manager", "ModemRemoved",
|
|
L_DBUS_MATCH_NONE, modem_removed_cb, NULL);
|
|
}
|
|
|
|
static void ofono_found(struct l_dbus *dbus, void *user_data)
|
|
{
|
|
struct l_dbus_message *message;
|
|
|
|
l_debug("");
|
|
|
|
/* start by getting all current modems */
|
|
message = l_dbus_message_new_method_call(dbus, "org.ofono", "/",
|
|
"org.ofono.Manager", "GetModems");
|
|
|
|
l_dbus_message_set_arguments(message, "");
|
|
|
|
l_dbus_send_with_reply(dbus, message, get_modems_cb, NULL, NULL);
|
|
}
|
|
|
|
static void ofono_disappeared(struct l_dbus *dbus, void *user_data)
|
|
{
|
|
l_debug("");
|
|
|
|
if (modems) {
|
|
l_queue_destroy(modems, modem_destroy);
|
|
modems = NULL;
|
|
|
|
l_dbus_remove_watch(dbus, modem_add_watch);
|
|
l_dbus_remove_watch(dbus, modem_removed_watch);
|
|
}
|
|
}
|
|
|
|
static int ofono_init(void)
|
|
{
|
|
struct l_dbus *dbus = dbus_get_bus();
|
|
|
|
ofono_watch = l_dbus_add_service_watch(dbus, "org.ofono", ofono_found,
|
|
ofono_disappeared, NULL, NULL);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void ofono_exit(void)
|
|
{
|
|
struct l_dbus *dbus = dbus_get_bus();
|
|
|
|
if (modems)
|
|
ofono_disappeared(dbus, NULL);
|
|
|
|
l_dbus_remove_watch(dbus, ofono_watch);
|
|
}
|
|
|
|
L_PLUGIN_DEFINE(__iwd_builtin_ofono, ofono, "oFono plugin", "1.0",
|
|
L_PLUGIN_PRIORITY_DEFAULT, ofono_init, ofono_exit)
|