ofono/src/modem.c

2347 lines
51 KiB
C

/*
*
* 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 <string.h>
#include <stdio.h>
#include <errno.h>
#include <glib.h>
#include <gdbus.h>
#include "ofono.h"
#include "common.h"
#define DEFAULT_POWERED_TIMEOUT (20)
static GSList *g_devinfo_drivers;
static GSList *g_driver_list;
static GSList *g_modem_list;
static int next_modem_id;
static gboolean powering_down;
static int modems_remaining;
static struct ofono_watchlist *g_modemwatches;
enum property_type {
PROPERTY_TYPE_INVALID = 0,
PROPERTY_TYPE_STRING,
PROPERTY_TYPE_INTEGER,
PROPERTY_TYPE_BOOLEAN,
};
enum modem_state {
MODEM_STATE_POWER_OFF,
MODEM_STATE_PRE_SIM,
MODEM_STATE_OFFLINE,
MODEM_STATE_ONLINE,
};
struct ofono_modem {
char *path;
enum modem_state modem_state;
GSList *atoms;
struct ofono_watchlist *atom_watches;
GSList *interface_list;
GSList *feature_list;
unsigned int call_ids;
DBusMessage *pending;
guint interface_update;
ofono_bool_t powered;
ofono_bool_t powered_pending;
ofono_bool_t get_online;
ofono_bool_t lockdown;
char *lock_owner;
guint lock_watch;
guint timeout;
guint timeout_hint;
ofono_bool_t online;
struct ofono_watchlist *online_watches;
struct ofono_watchlist *powered_watches;
guint emergency;
GHashTable *properties;
struct ofono_sim *sim;
unsigned int sim_watch;
unsigned int sim_ready_watch;
const struct ofono_modem_driver *driver;
void *driver_data;
char *driver_type;
char *name;
};
struct ofono_devinfo {
char *manufacturer;
char *model;
char *revision;
char *serial;
char *svn;
unsigned int dun_watch;
const struct ofono_devinfo_driver *driver;
void *driver_data;
struct ofono_atom *atom;
};
struct ofono_atom {
enum ofono_atom_type type;
enum modem_state modem_state;
void (*destruct)(struct ofono_atom *atom);
void (*unregister)(struct ofono_atom *atom);
void *data;
struct ofono_modem *modem;
};
struct atom_watch {
struct ofono_watchlist_item item;
enum ofono_atom_type type;
};
struct modem_property {
enum property_type type;
void *value;
};
static const char *modem_type_to_string(enum ofono_modem_type type)
{
switch (type) {
case OFONO_MODEM_TYPE_HARDWARE:
return "hardware";
case OFONO_MODEM_TYPE_HFP:
return "hfp";
case OFONO_MODEM_TYPE_SAP:
return "sap";
case OFONO_MODEM_TYPE_TEST:
return "test";
}
return "unknown";
}
unsigned int __ofono_modem_callid_next(struct ofono_modem *modem)
{
unsigned int i;
for (i = 1; i < sizeof(modem->call_ids) * 8; i++) {
if (modem->call_ids & (1 << i))
continue;
return i;
}
return 0;
}
void __ofono_modem_callid_hold(struct ofono_modem *modem, int id)
{
modem->call_ids |= (1 << id);
}
void __ofono_modem_callid_release(struct ofono_modem *modem, int id)
{
modem->call_ids &= ~(1 << id);
}
void ofono_modem_set_data(struct ofono_modem *modem, void *data)
{
if (modem == NULL)
return;
modem->driver_data = data;
}
void *ofono_modem_get_data(struct ofono_modem *modem)
{
if (modem == NULL)
return NULL;
return modem->driver_data;
}
const char *ofono_modem_get_path(struct ofono_modem *modem)
{
if (modem)
return modem->path;
return NULL;
}
struct ofono_sim *ofono_modem_get_sim(struct ofono_modem *modem)
{
return __ofono_atom_find(OFONO_ATOM_TYPE_SIM, modem);
}
struct ofono_gprs *ofono_modem_get_gprs(struct ofono_modem *modem)
{
return __ofono_atom_find(OFONO_ATOM_TYPE_GPRS, modem);
}
struct ofono_voicecall *ofono_modem_get_voicecall(struct ofono_modem *modem)
{
return __ofono_atom_find(OFONO_ATOM_TYPE_VOICECALL, modem);
}
struct ofono_atom *__ofono_modem_add_atom(struct ofono_modem *modem,
enum ofono_atom_type type,
void (*destruct)(struct ofono_atom *),
void *data)
{
struct ofono_atom *atom;
if (modem == NULL)
return NULL;
atom = g_new0(struct ofono_atom, 1);
atom->type = type;
atom->modem_state = modem->modem_state;
atom->destruct = destruct;
atom->data = data;
atom->modem = modem;
modem->atoms = g_slist_prepend(modem->atoms, atom);
return atom;
}
struct ofono_atom *__ofono_modem_add_atom_offline(struct ofono_modem *modem,
enum ofono_atom_type type,
void (*destruct)(struct ofono_atom *),
void *data)
{
struct ofono_atom *atom;
atom = __ofono_modem_add_atom(modem, type, destruct, data);
atom->modem_state = MODEM_STATE_OFFLINE;
return atom;
}
void *__ofono_atom_get_data(struct ofono_atom *atom)
{
return atom->data;
}
const char *__ofono_atom_get_path(struct ofono_atom *atom)
{
return atom->modem->path;
}
struct ofono_modem *__ofono_atom_get_modem(struct ofono_atom *atom)
{
return atom->modem;
}
static void call_watches(struct ofono_atom *atom,
enum ofono_atom_watch_condition cond)
{
struct ofono_modem *modem = atom->modem;
GSList *atom_watches = modem->atom_watches->items;
GSList *l;
struct atom_watch *watch;
ofono_atom_watch_func notify;
for (l = atom_watches; l; l = l->next) {
watch = l->data;
if (watch->type != atom->type)
continue;
notify = watch->item.notify;
notify(atom, cond, watch->item.notify_data);
}
}
void __ofono_atom_register(struct ofono_atom *atom,
void (*unregister)(struct ofono_atom *))
{
if (unregister == NULL)
return;
atom->unregister = unregister;
call_watches(atom, OFONO_ATOM_WATCH_CONDITION_REGISTERED);
}
void __ofono_atom_unregister(struct ofono_atom *atom)
{
if (atom->unregister == NULL)
return;
call_watches(atom, OFONO_ATOM_WATCH_CONDITION_UNREGISTERED);
atom->unregister(atom);
atom->unregister = NULL;
}
gboolean __ofono_atom_get_registered(struct ofono_atom *atom)
{
return atom->unregister ? TRUE : FALSE;
}
unsigned int __ofono_modem_add_atom_watch(struct ofono_modem *modem,
enum ofono_atom_type type,
ofono_atom_watch_func notify,
void *data, ofono_destroy_func destroy)
{
struct atom_watch *watch;
unsigned int id;
GSList *l;
struct ofono_atom *atom;
if (notify == NULL)
return 0;
watch = g_new0(struct atom_watch, 1);
watch->type = type;
watch->item.notify = notify;
watch->item.destroy = destroy;
watch->item.notify_data = data;
id = __ofono_watchlist_add_item(modem->atom_watches,
(struct ofono_watchlist_item *)watch);
for (l = modem->atoms; l; l = l->next) {
atom = l->data;
if (atom->type != type || atom->unregister == NULL)
continue;
notify(atom, OFONO_ATOM_WATCH_CONDITION_REGISTERED, data);
}
return id;
}
gboolean __ofono_modem_remove_atom_watch(struct ofono_modem *modem,
unsigned int id)
{
return __ofono_watchlist_remove_item(modem->atom_watches, id);
}
struct ofono_atom *__ofono_modem_find_atom(struct ofono_modem *modem,
enum ofono_atom_type type)
{
GSList *l;
struct ofono_atom *atom;
if (modem == NULL)
return NULL;
for (l = modem->atoms; l; l = l->next) {
atom = l->data;
if (atom->type == type && atom->unregister != NULL)
return atom;
}
return NULL;
}
void __ofono_modem_foreach_atom(struct ofono_modem *modem,
enum ofono_atom_type type,
ofono_atom_func callback, void *data)
{
GSList *l;
struct ofono_atom *atom;
if (modem == NULL)
return;
for (l = modem->atoms; l; l = l->next) {
atom = l->data;
if (atom->type != type)
continue;
callback(atom, data);
}
}
void __ofono_modem_foreach_registered_atom(struct ofono_modem *modem,
enum ofono_atom_type type,
ofono_atom_func callback,
void *data)
{
GSList *l;
struct ofono_atom *atom;
if (modem == NULL)
return;
for (l = modem->atoms; l; l = l->next) {
atom = l->data;
if (atom->type != type)
continue;
if (atom->unregister == NULL)
continue;
callback(atom, data);
}
}
void __ofono_atom_free(struct ofono_atom *atom)
{
struct ofono_modem *modem = atom->modem;
modem->atoms = g_slist_remove(modem->atoms, atom);
__ofono_atom_unregister(atom);
if (atom->destruct)
atom->destruct(atom);
g_free(atom);
}
static void flush_atoms(struct ofono_modem *modem, enum modem_state new_state)
{
GSList *cur;
GSList *prev;
GSList *tmp;
DBG("");
prev = NULL;
cur = modem->atoms;
while (cur) {
struct ofono_atom *atom = cur->data;
if (atom->modem_state <= new_state) {
prev = cur;
cur = cur->next;
continue;
}
__ofono_atom_unregister(atom);
if (atom->destruct)
atom->destruct(atom);
g_free(atom);
if (prev)
prev->next = cur->next;
else
modem->atoms = cur->next;
tmp = cur;
cur = cur->next;
g_slist_free_1(tmp);
}
}
static void notify_online_watches(struct ofono_modem *modem)
{
struct ofono_watchlist_item *item;
GSList *l;
ofono_modem_online_notify_func notify;
if (modem->online_watches == NULL)
return;
for (l = modem->online_watches->items; l; l = l->next) {
item = l->data;
notify = item->notify;
notify(modem, modem->online, item->notify_data);
}
}
static void notify_powered_watches(struct ofono_modem *modem)
{
struct ofono_watchlist_item *item;
GSList *l;
ofono_modem_powered_notify_func notify;
if (modem->powered_watches == NULL)
return;
for (l = modem->powered_watches->items; l; l = l->next) {
item = l->data;
notify = item->notify;
notify(modem, modem->powered, item->notify_data);
}
}
static void set_online(struct ofono_modem *modem, ofono_bool_t new_online)
{
DBusConnection *conn = ofono_dbus_get_connection();
if (new_online == modem->online)
return;
modem->online = new_online;
ofono_dbus_signal_property_changed(conn, modem->path,
OFONO_MODEM_INTERFACE,
"Online", DBUS_TYPE_BOOLEAN,
&modem->online);
notify_online_watches(modem);
}
static void modem_change_state(struct ofono_modem *modem,
enum modem_state new_state)
{
struct ofono_modem_driver const *driver = modem->driver;
enum modem_state old_state = modem->modem_state;
DBG("old state: %d, new state: %d", old_state, new_state);
if (old_state == new_state)
return;
modem->modem_state = new_state;
if (old_state > new_state)
flush_atoms(modem, new_state);
switch (new_state) {
case MODEM_STATE_POWER_OFF:
modem->call_ids = 0;
break;
case MODEM_STATE_PRE_SIM:
if (old_state < MODEM_STATE_PRE_SIM && driver->pre_sim)
driver->pre_sim(modem);
break;
case MODEM_STATE_OFFLINE:
if (old_state < MODEM_STATE_OFFLINE) {
if (driver->post_sim)
driver->post_sim(modem);
__ofono_history_probe_drivers(modem);
__ofono_nettime_probe_drivers(modem);
}
break;
case MODEM_STATE_ONLINE:
if (driver->post_online)
driver->post_online(modem);
break;
}
}
unsigned int __ofono_modem_add_online_watch(struct ofono_modem *modem,
ofono_modem_online_notify_func notify,
void *data, ofono_destroy_func destroy)
{
struct ofono_watchlist_item *item;
if (modem == NULL || notify == NULL)
return 0;
item = g_new0(struct ofono_watchlist_item, 1);
item->notify = notify;
item->destroy = destroy;
item->notify_data = data;
return __ofono_watchlist_add_item(modem->online_watches, item);
}
void __ofono_modem_remove_online_watch(struct ofono_modem *modem,
unsigned int id)
{
__ofono_watchlist_remove_item(modem->online_watches, id);
}
unsigned int __ofono_modem_add_powered_watch(struct ofono_modem *modem,
ofono_modem_powered_notify_func notify,
void *data, ofono_destroy_func destroy)
{
struct ofono_watchlist_item *item;
if (modem == NULL || notify == NULL)
return 0;
item = g_new0(struct ofono_watchlist_item, 1);
item->notify = notify;
item->destroy = destroy;
item->notify_data = data;
return __ofono_watchlist_add_item(modem->powered_watches, item);
}
void __ofono_modem_remove_powered_watch(struct ofono_modem *modem,
unsigned int id)
{
__ofono_watchlist_remove_item(modem->powered_watches, id);
}
static gboolean modem_has_sim(struct ofono_modem *modem)
{
GSList *l;
struct ofono_atom *atom;
for (l = modem->atoms; l; l = l->next) {
atom = l->data;
if (atom->type == OFONO_ATOM_TYPE_SIM)
return TRUE;
}
return FALSE;
}
static gboolean modem_is_always_online(struct ofono_modem *modem)
{
if (modem->driver->set_online == NULL)
return TRUE;
if (ofono_modem_get_boolean(modem, "AlwaysOnline") == TRUE)
return TRUE;
return FALSE;
}
static void common_online_cb(const struct ofono_error *error, void *data)
{
struct ofono_modem *modem = data;
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
return;
/*
* If we need to get online after a silent reset this callback
* is called. The callback should not consider the pending dbus
* message.
*
* Additionally, this process can be interrupted by the following
* events:
* - Sim being removed or reset
* - SetProperty(Powered, False) being called
* - SetProperty(Lockdown, True) being called
*
* We should not set the modem to the online state in these cases.
*/
switch (modem->modem_state) {
case MODEM_STATE_OFFLINE:
set_online(modem, TRUE);
/* Will this increase emergency call setup time??? */
modem_change_state(modem, MODEM_STATE_ONLINE);
break;
case MODEM_STATE_POWER_OFF:
/* The powered operation is pending */
break;
case MODEM_STATE_PRE_SIM:
/*
* Its valid to be in online even without a SIM/SIM being
* PIN locked. e.g.: Emergency mode
*/
DBG("Online in PRE SIM state");
set_online(modem, TRUE);
break;
case MODEM_STATE_ONLINE:
ofono_error("Online called when the modem is already online!");
break;
};
}
static void online_cb(const struct ofono_error *error, void *data)
{
struct ofono_modem *modem = data;
DBusMessage *reply;
if (!modem->pending)
goto out;
if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
reply = dbus_message_new_method_return(modem->pending);
else
reply = __ofono_error_failed(modem->pending);
__ofono_dbus_pending_reply(&modem->pending, reply);
out:
common_online_cb(error, data);
}
static void offline_cb(const struct ofono_error *error, void *data)
{
struct ofono_modem *modem = data;
DBusMessage *reply;
if (error->type == OFONO_ERROR_TYPE_NO_ERROR)
reply = dbus_message_new_method_return(modem->pending);
else
reply = __ofono_error_failed(modem->pending);
__ofono_dbus_pending_reply(&modem->pending, reply);
if (error->type == OFONO_ERROR_TYPE_NO_ERROR) {
switch (modem->modem_state) {
case MODEM_STATE_PRE_SIM:
set_online(modem, FALSE);
break;
case MODEM_STATE_ONLINE:
set_online(modem, FALSE);
modem_change_state(modem, MODEM_STATE_OFFLINE);
break;
default:
break;
}
}
}
static void sim_state_watch(enum ofono_sim_state new_state, void *user)
{
struct ofono_modem *modem = user;
switch (new_state) {
case OFONO_SIM_STATE_NOT_PRESENT:
modem_change_state(modem, MODEM_STATE_PRE_SIM);
case OFONO_SIM_STATE_INSERTED:
case OFONO_SIM_STATE_RESETTING:
break;
case OFONO_SIM_STATE_LOCKED_OUT:
modem_change_state(modem, MODEM_STATE_PRE_SIM);
break;
case OFONO_SIM_STATE_READY:
modem_change_state(modem, MODEM_STATE_OFFLINE);
/* Modem is always online, proceed to online state. */
if (modem_is_always_online(modem) == TRUE)
set_online(modem, TRUE);
if (modem->online == TRUE)
modem_change_state(modem, MODEM_STATE_ONLINE);
else if (modem->get_online)
modem->driver->set_online(modem, 1, common_online_cb,
modem);
modem->get_online = FALSE;
break;
}
}
static DBusMessage *set_property_online(struct ofono_modem *modem,
DBusMessage *msg,
DBusMessageIter *var)
{
ofono_bool_t online;
const struct ofono_modem_driver *driver = modem->driver;
if (modem->powered == FALSE)
return __ofono_error_not_available(msg);
if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_BOOLEAN)
return __ofono_error_invalid_args(msg);
dbus_message_iter_get_basic(var, &online);
if (modem->pending != NULL)
return __ofono_error_busy(msg);
if (modem->online == online)
return dbus_message_new_method_return(msg);
if (ofono_modem_get_emergency_mode(modem) == TRUE)
return __ofono_error_emergency_active(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);
driver->set_online(modem, online,
online ? online_cb : offline_cb, modem);
return NULL;
}
ofono_bool_t ofono_modem_get_online(struct ofono_modem *modem)
{
if (modem == NULL)
return FALSE;
return modem->online;
}
void __ofono_modem_append_properties(struct ofono_modem *modem,
DBusMessageIter *dict)
{
char **interfaces;
char **features;
int i;
GSList *l;
struct ofono_devinfo *info;
dbus_bool_t emergency = ofono_modem_get_emergency_mode(modem);
const char *strtype;
const char *system_path;
ofono_dbus_dict_append(dict, "Online", DBUS_TYPE_BOOLEAN,
&modem->online);
ofono_dbus_dict_append(dict, "Powered", DBUS_TYPE_BOOLEAN,
&modem->powered);
ofono_dbus_dict_append(dict, "Lockdown", DBUS_TYPE_BOOLEAN,
&modem->lockdown);
ofono_dbus_dict_append(dict, "Emergency", DBUS_TYPE_BOOLEAN,
&emergency);
info = __ofono_atom_find(OFONO_ATOM_TYPE_DEVINFO, modem);
if (info) {
if (info->manufacturer)
ofono_dbus_dict_append(dict, "Manufacturer",
DBUS_TYPE_STRING,
&info->manufacturer);
if (info->model)
ofono_dbus_dict_append(dict, "Model", DBUS_TYPE_STRING,
&info->model);
if (info->revision)
ofono_dbus_dict_append(dict, "Revision",
DBUS_TYPE_STRING,
&info->revision);
if (info->serial)
ofono_dbus_dict_append(dict, "Serial",
DBUS_TYPE_STRING,
&info->serial);
if (info->svn)
ofono_dbus_dict_append(dict, "SoftwareVersionNumber",
DBUS_TYPE_STRING,
&info->svn);
}
system_path = ofono_modem_get_string(modem, "SystemPath");
if (system_path)
ofono_dbus_dict_append(dict, "SystemPath", DBUS_TYPE_STRING,
&system_path);
interfaces = g_new0(char *, g_slist_length(modem->interface_list) + 1);
for (i = 0, l = modem->interface_list; l; l = l->next, i++)
interfaces[i] = l->data;
ofono_dbus_dict_append_array(dict, "Interfaces", DBUS_TYPE_STRING,
&interfaces);
g_free(interfaces);
features = g_new0(char *, g_slist_length(modem->feature_list) + 1);
for (i = 0, l = modem->feature_list; l; l = l->next, i++)
features[i] = l->data;
ofono_dbus_dict_append_array(dict, "Features", DBUS_TYPE_STRING,
&features);
g_free(features);
if (modem->name)
ofono_dbus_dict_append(dict, "Name", DBUS_TYPE_STRING,
&modem->name);
strtype = modem_type_to_string(modem->driver->modem_type);
ofono_dbus_dict_append(dict, "Type", DBUS_TYPE_STRING, &strtype);
}
static DBusMessage *modem_get_properties(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ofono_modem *modem = data;
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_modem_append_properties(modem, &dict);
dbus_message_iter_close_container(&iter, &dict);
return reply;
}
static int set_powered(struct ofono_modem *modem, ofono_bool_t powered)
{
const struct ofono_modem_driver *driver = modem->driver;
int err = -EINVAL;
if (modem->powered_pending == powered)
return -EALREADY;
/* Remove the atoms even if the driver is no longer available */
if (powered == FALSE)
modem_change_state(modem, MODEM_STATE_POWER_OFF);
modem->powered_pending = powered;
if (driver == NULL)
return -EINVAL;
if (powered == TRUE) {
if (driver->enable)
err = driver->enable(modem);
} else {
if (driver->disable)
err = driver->disable(modem);
}
if (err == 0) {
modem->powered = powered;
notify_powered_watches(modem);
} else if (err != -EINPROGRESS)
modem->powered_pending = modem->powered;
return err;
}
static void lockdown_remove(struct ofono_modem *modem)
{
DBusConnection *conn = ofono_dbus_get_connection();
if (modem->lock_watch) {
g_dbus_remove_watch(conn, modem->lock_watch);
modem->lock_watch = 0;
}
g_free(modem->lock_owner);
modem->lock_owner = NULL;
modem->lockdown = FALSE;
}
static gboolean set_powered_timeout(gpointer user)
{
struct ofono_modem *modem = user;
DBG("modem: %p", modem);
modem->timeout = 0;
if (modem->powered_pending == FALSE) {
DBusConnection *conn = ofono_dbus_get_connection();
dbus_bool_t powered = FALSE;
set_online(modem, FALSE);
modem->powered = FALSE;
notify_powered_watches(modem);
ofono_dbus_signal_property_changed(conn, modem->path,
OFONO_MODEM_INTERFACE,
"Powered", DBUS_TYPE_BOOLEAN,
&powered);
} else {
modem->powered_pending = modem->powered;
}
if (modem->pending != NULL) {
DBusMessage *reply;
reply = __ofono_error_timed_out(modem->pending);
__ofono_dbus_pending_reply(&modem->pending, reply);
if (modem->lockdown)
lockdown_remove(modem);
}
return FALSE;
}
static void lockdown_disconnect(DBusConnection *conn, void *user_data)
{
struct ofono_modem *modem = user_data;
DBG("");
ofono_dbus_signal_property_changed(conn, modem->path,
OFONO_MODEM_INTERFACE,
"Lockdown", DBUS_TYPE_BOOLEAN,
&modem->lockdown);
modem->lock_watch = 0;
lockdown_remove(modem);
}
static DBusMessage *set_property_lockdown(struct ofono_modem *modem,
DBusMessage *msg,
DBusMessageIter *var)
{
DBusConnection *conn = ofono_dbus_get_connection();
ofono_bool_t lockdown;
dbus_bool_t powered;
const char *caller;
int err;
if (dbus_message_iter_get_arg_type(var) != DBUS_TYPE_BOOLEAN)
return __ofono_error_invalid_args(msg);
dbus_message_iter_get_basic(var, &lockdown);
if (modem->pending != NULL)
return __ofono_error_busy(msg);
caller = dbus_message_get_sender(msg);
if (modem->lockdown && g_strcmp0(caller, modem->lock_owner))
return __ofono_error_access_denied(msg);
if (modem->lockdown == lockdown)
return dbus_message_new_method_return(msg);
if (lockdown == FALSE) {
lockdown_remove(modem);
goto done;
}
if (ofono_modem_get_emergency_mode(modem) == TRUE)
return __ofono_error_emergency_active(msg);
modem->lock_owner = g_strdup(caller);
modem->lock_watch = g_dbus_add_disconnect_watch(conn,
modem->lock_owner, lockdown_disconnect,
modem, NULL);
if (modem->lock_watch == 0) {
g_free(modem->lock_owner);
modem->lock_owner = NULL;
return __ofono_error_failed(msg);
}
modem->lockdown = lockdown;
if (modem->powered == FALSE)
goto done;
err = set_powered(modem, FALSE);
if (err < 0) {
if (err != -EINPROGRESS) {
lockdown_remove(modem);
return __ofono_error_failed(msg);
}
modem->pending = dbus_message_ref(msg);
modem->timeout = g_timeout_add_seconds(modem->timeout_hint,
set_powered_timeout, modem);
return NULL;
}
set_online(modem, FALSE);
powered = FALSE;
ofono_dbus_signal_property_changed(conn, modem->path,
OFONO_MODEM_INTERFACE,
"Powered", DBUS_TYPE_BOOLEAN,
&powered);
done:
g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
ofono_dbus_signal_property_changed(conn, modem->path,
OFONO_MODEM_INTERFACE,
"Lockdown", DBUS_TYPE_BOOLEAN,
&lockdown);
return NULL;
}
static DBusMessage *modem_set_property(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct ofono_modem *modem = data;
DBusMessageIter iter, var;
const char *name;
if (dbus_message_iter_init(msg, &iter) == FALSE)
return __ofono_error_invalid_args(msg);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
return __ofono_error_invalid_args(msg);
dbus_message_iter_get_basic(&iter, &name);
dbus_message_iter_next(&iter);
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
return __ofono_error_invalid_args(msg);
if (powering_down == TRUE)
return __ofono_error_failed(msg);
dbus_message_iter_recurse(&iter, &var);
if (g_str_equal(name, "Online"))
return set_property_online(modem, msg, &var);
if (g_str_equal(name, "Powered") == TRUE) {
ofono_bool_t powered;
int err;
if (dbus_message_iter_get_arg_type(&var) != DBUS_TYPE_BOOLEAN)
return __ofono_error_invalid_args(msg);
dbus_message_iter_get_basic(&var, &powered);
if (modem->pending != NULL)
return __ofono_error_busy(msg);
if (modem->powered == powered)
return dbus_message_new_method_return(msg);
if (ofono_modem_get_emergency_mode(modem) == TRUE)
return __ofono_error_emergency_active(msg);
if (modem->lockdown)
return __ofono_error_access_denied(msg);
if (!powered)
__ofono_sim_clear_cached_pins(modem->sim);
err = set_powered(modem, powered);
if (err < 0) {
if (err != -EINPROGRESS)
return __ofono_error_failed(msg);
modem->pending = dbus_message_ref(msg);
modem->timeout = g_timeout_add_seconds(
modem->timeout_hint,
set_powered_timeout, modem);
return NULL;
}
g_dbus_send_reply(conn, msg, DBUS_TYPE_INVALID);
ofono_dbus_signal_property_changed(conn, modem->path,
OFONO_MODEM_INTERFACE,
"Powered", DBUS_TYPE_BOOLEAN,
&powered);
if (powered) {
modem_change_state(modem, MODEM_STATE_PRE_SIM);
/* Force SIM Ready for devies with no sim atom */
if (modem_has_sim(modem) == FALSE)
sim_state_watch(OFONO_SIM_STATE_READY, modem);
} else {
set_online(modem, FALSE);
modem_change_state(modem, MODEM_STATE_POWER_OFF);
}
return NULL;
}
if (g_str_equal(name, "Lockdown"))
return set_property_lockdown(modem, msg, &var);
return __ofono_error_invalid_args(msg);
}
static const GDBusMethodTable modem_methods[] = {
{ GDBUS_METHOD("GetProperties",
NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
modem_get_properties) },
{ GDBUS_ASYNC_METHOD("SetProperty",
GDBUS_ARGS({ "property", "s" }, { "value", "v" }),
NULL, modem_set_property) },
{ }
};
static const GDBusSignalTable modem_signals[] = {
{ GDBUS_SIGNAL("PropertyChanged",
GDBUS_ARGS({ "name", "s" }, { "value", "v" })) },
{ }
};
void ofono_modem_set_powered(struct ofono_modem *modem, ofono_bool_t powered)
{
DBusConnection *conn = ofono_dbus_get_connection();
dbus_bool_t dbus_powered = powered;
if (modem->timeout > 0) {
g_source_remove(modem->timeout);
modem->timeout = 0;
}
if (modem->powered_pending != modem->powered &&
modem->pending != NULL) {
DBusMessage *reply;
if (powered == modem->powered_pending)
reply = dbus_message_new_method_return(modem->pending);
else
reply = __ofono_error_failed(modem->pending);
__ofono_dbus_pending_reply(&modem->pending, reply);
}
modem->powered_pending = powered;
if (modem->powered == powered)
goto out;
modem->powered = powered;
notify_powered_watches(modem);
if (modem->lockdown)
ofono_dbus_signal_property_changed(conn, modem->path,
OFONO_MODEM_INTERFACE,
"Lockdown", DBUS_TYPE_BOOLEAN,
&modem->lockdown);
if (modem->driver == NULL) {
ofono_error("Calling ofono_modem_set_powered on a"
"modem with no driver is not valid, "
"please fix the modem driver.");
return;
}
ofono_dbus_signal_property_changed(conn, modem->path,
OFONO_MODEM_INTERFACE,
"Powered", DBUS_TYPE_BOOLEAN,
&dbus_powered);
if (powered) {
modem_change_state(modem, MODEM_STATE_PRE_SIM);
/* Force SIM Ready for devices with no sim atom */
if (modem_has_sim(modem) == FALSE)
sim_state_watch(OFONO_SIM_STATE_READY, modem);
} else {
set_online(modem, FALSE);
modem_change_state(modem, MODEM_STATE_POWER_OFF);
}
out:
if (powering_down && powered == FALSE) {
modems_remaining -= 1;
if (modems_remaining == 0)
__ofono_exit();
}
}
ofono_bool_t ofono_modem_get_powered(struct ofono_modem *modem)
{
if (modem == NULL)
return FALSE;
return modem->powered;
}
static gboolean trigger_interface_update(void *data)
{
struct ofono_modem *modem = data;
DBusConnection *conn = ofono_dbus_get_connection();
char **interfaces;
char **features;
GSList *l;
int i;
interfaces = g_new0(char *, g_slist_length(modem->interface_list) + 1);
for (i = 0, l = modem->interface_list; l; l = l->next, i++)
interfaces[i] = l->data;
ofono_dbus_signal_array_property_changed(conn, modem->path,
OFONO_MODEM_INTERFACE,
"Interfaces", DBUS_TYPE_STRING,
&interfaces);
g_free(interfaces);
features = g_new0(char *, g_slist_length(modem->feature_list) + 1);
for (i = 0, l = modem->feature_list; l; l = l->next, i++)
features[i] = l->data;
ofono_dbus_signal_array_property_changed(conn, modem->path,
OFONO_MODEM_INTERFACE,
"Features", DBUS_TYPE_STRING,
&features);
g_free(features);
modem->interface_update = 0;
return FALSE;
}
static const struct {
const char *interface;
const char *feature;
} feature_map[] = {
{ OFONO_NETWORK_REGISTRATION_INTERFACE, "net" },
{ OFONO_RADIO_SETTINGS_INTERFACE, "rat" },
{ OFONO_CELL_BROADCAST_INTERFACE, "cbs" },
{ OFONO_MESSAGE_MANAGER_INTERFACE, "sms" },
{ OFONO_SIM_MANAGER_INTERFACE, "sim" },
{ OFONO_STK_INTERFACE, "stk" },
{ OFONO_SUPPLEMENTARY_SERVICES_INTERFACE, "ussd" },
{ OFONO_CONNECTION_MANAGER_INTERFACE, "gprs" },
{ OFONO_TEXT_TELEPHONY_INTERFACE, "tty" },
{ OFONO_LOCATION_REPORTING_INTERFACE, "gps" },
{ },
};
static const char *get_feature(const char *interface)
{
int i;
for (i = 0; feature_map[i].interface; i++) {
if (strcmp(feature_map[i].interface, interface) == 0)
return feature_map[i].feature;
}
return NULL;
}
void ofono_modem_add_interface(struct ofono_modem *modem,
const char *interface)
{
const char *feature;
modem->interface_list = g_slist_prepend(modem->interface_list,
g_strdup(interface));
feature = get_feature(interface);
if (feature)
modem->feature_list = g_slist_prepend(modem->feature_list,
g_strdup(feature));
if (modem->interface_update != 0)
return;
modem->interface_update = g_idle_add(trigger_interface_update, modem);
}
void ofono_modem_remove_interface(struct ofono_modem *modem,
const char *interface)
{
GSList *found;
const char *feature;
found = g_slist_find_custom(modem->interface_list, interface,
(GCompareFunc) strcmp);
if (found == NULL) {
ofono_error("Interface %s not found on the interface_list",
interface);
return;
}
g_free(found->data);
modem->interface_list = g_slist_remove(modem->interface_list,
found->data);
feature = get_feature(interface);
if (feature) {
found = g_slist_find_custom(modem->feature_list, feature,
(GCompareFunc) strcmp);
if (found) {
g_free(found->data);
modem->feature_list =
g_slist_remove(modem->feature_list,
found->data);
}
}
if (modem->interface_update != 0)
return;
modem->interface_update = g_idle_add(trigger_interface_update, modem);
}
static void query_svn_cb(const struct ofono_error *error,
const char *svn, void *user)
{
struct ofono_devinfo *info = user;
DBusConnection *conn = ofono_dbus_get_connection();
const char *path = __ofono_atom_get_path(info->atom);
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
return;
info->svn = g_strdup(svn);
ofono_dbus_signal_property_changed(conn, path, OFONO_MODEM_INTERFACE,
"SoftwareVersionNumber", DBUS_TYPE_STRING, &info->svn);
}
static void query_svn(struct ofono_devinfo *info)
{
if (info->driver->query_svn == NULL)
return;
info->driver->query_svn(info, query_svn_cb, info);
}
static void query_serial_cb(const struct ofono_error *error,
const char *serial, void *user)
{
struct ofono_devinfo *info = user;
DBusConnection *conn = ofono_dbus_get_connection();
const char *path = __ofono_atom_get_path(info->atom);
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
goto out;
info->serial = g_strdup(serial);
ofono_dbus_signal_property_changed(conn, path,
OFONO_MODEM_INTERFACE,
"Serial", DBUS_TYPE_STRING,
&info->serial);
out:
query_svn(info);
}
static void query_serial(struct ofono_devinfo *info)
{
if (info->driver->query_serial == NULL)
return;
info->driver->query_serial(info, query_serial_cb, info);
}
static void query_revision_cb(const struct ofono_error *error,
const char *revision, void *user)
{
struct ofono_devinfo *info = user;
DBusConnection *conn = ofono_dbus_get_connection();
const char *path = __ofono_atom_get_path(info->atom);
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
goto out;
info->revision = g_strdup(revision);
ofono_dbus_signal_property_changed(conn, path,
OFONO_MODEM_INTERFACE,
"Revision", DBUS_TYPE_STRING,
&info->revision);
out:
query_serial(info);
}
static void query_revision(struct ofono_devinfo *info)
{
if (info->driver->query_revision == NULL) {
query_serial(info);
return;
}
info->driver->query_revision(info, query_revision_cb, info);
}
static void query_model_cb(const struct ofono_error *error,
const char *model, void *user)
{
struct ofono_devinfo *info = user;
DBusConnection *conn = ofono_dbus_get_connection();
const char *path = __ofono_atom_get_path(info->atom);
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
goto out;
info->model = g_strdup(model);
ofono_dbus_signal_property_changed(conn, path,
OFONO_MODEM_INTERFACE,
"Model", DBUS_TYPE_STRING,
&info->model);
out:
query_revision(info);
}
static void query_model(struct ofono_devinfo *info)
{
if (info->driver->query_model == NULL) {
/* If model is not supported, don't bother querying revision */
query_serial(info);
return;
}
info->driver->query_model(info, query_model_cb, info);
}
static void query_manufacturer_cb(const struct ofono_error *error,
const char *manufacturer, void *user)
{
struct ofono_devinfo *info = user;
DBusConnection *conn = ofono_dbus_get_connection();
const char *path = __ofono_atom_get_path(info->atom);
if (error->type != OFONO_ERROR_TYPE_NO_ERROR)
goto out;
info->manufacturer = g_strdup(manufacturer);
ofono_dbus_signal_property_changed(conn, path,
OFONO_MODEM_INTERFACE,
"Manufacturer",
DBUS_TYPE_STRING,
&info->manufacturer);
out:
query_model(info);
}
static gboolean query_manufacturer(gpointer user)
{
struct ofono_devinfo *info = user;
if (info->driver->query_manufacturer == NULL) {
query_model(info);
return FALSE;
}
info->driver->query_manufacturer(info, query_manufacturer_cb, info);
return FALSE;
}
static void attr_template(struct ofono_emulator *em,
struct ofono_emulator_request *req,
const char *attr)
{
struct ofono_error result;
if (attr == NULL)
attr = "Unknown";
result.error = 0;
switch (ofono_emulator_request_get_type(req)) {
case OFONO_EMULATOR_REQUEST_TYPE_COMMAND_ONLY:
ofono_emulator_send_info(em, attr, TRUE);
result.type = OFONO_ERROR_TYPE_NO_ERROR;
ofono_emulator_send_final(em, &result);
break;
case OFONO_EMULATOR_REQUEST_TYPE_SUPPORT:
result.type = OFONO_ERROR_TYPE_NO_ERROR;
ofono_emulator_send_final(em, &result);
break;
default:
result.type = OFONO_ERROR_TYPE_FAILURE;
ofono_emulator_send_final(em, &result);
};
}
static void gmi_cb(struct ofono_emulator *em,
struct ofono_emulator_request *req, void *userdata)
{
struct ofono_devinfo *info = userdata;
attr_template(em, req, info->manufacturer);
}
static void gmm_cb(struct ofono_emulator *em,
struct ofono_emulator_request *req, void *userdata)
{
struct ofono_devinfo *info = userdata;
attr_template(em, req, info->model);
}
static void gmr_cb(struct ofono_emulator *em,
struct ofono_emulator_request *req, void *userdata)
{
struct ofono_devinfo *info = userdata;
attr_template(em, req, info->revision);
}
static void gcap_cb(struct ofono_emulator *em,
struct ofono_emulator_request *req, void *userdata)
{
attr_template(em, req, "+GCAP: +CGSM");
}
static void dun_watch(struct ofono_atom *atom,
enum ofono_atom_watch_condition cond, void *data)
{
struct ofono_emulator *em = __ofono_atom_get_data(atom);
if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED)
return;
ofono_emulator_add_handler(em, "+GMI", gmi_cb, data, NULL);
ofono_emulator_add_handler(em, "+GMM", gmm_cb, data, NULL);
ofono_emulator_add_handler(em, "+GMR", gmr_cb, data, NULL);
ofono_emulator_add_handler(em, "+GCAP", gcap_cb, data, NULL);
}
int ofono_devinfo_driver_register(const struct ofono_devinfo_driver *d)
{
DBG("driver: %p, name: %s", d, d->name);
if (d->probe == NULL)
return -EINVAL;
g_devinfo_drivers = g_slist_prepend(g_devinfo_drivers, (void *) d);
return 0;
}
void ofono_devinfo_driver_unregister(const struct ofono_devinfo_driver *d)
{
DBG("driver: %p, name: %s", d, d->name);
g_devinfo_drivers = g_slist_remove(g_devinfo_drivers, (void *) d);
}
static void devinfo_remove(struct ofono_atom *atom)
{
struct ofono_devinfo *info = __ofono_atom_get_data(atom);
DBG("atom: %p", atom);
if (info == NULL)
return;
if (info->driver == NULL)
return;
if (info->driver->remove)
info->driver->remove(info);
g_free(info);
}
struct ofono_devinfo *ofono_devinfo_create(struct ofono_modem *modem,
unsigned int vendor,
const char *driver,
void *data)
{
struct ofono_devinfo *info;
GSList *l;
info = g_new0(struct ofono_devinfo, 1);
info->atom = __ofono_modem_add_atom(modem, OFONO_ATOM_TYPE_DEVINFO,
devinfo_remove, info);
for (l = g_devinfo_drivers; l; l = l->next) {
const struct ofono_devinfo_driver *drv = l->data;
if (g_strcmp0(drv->name, driver))
continue;
if (drv->probe(info, vendor, data) < 0)
continue;
info->driver = drv;
break;
}
return info;
}
static void devinfo_unregister(struct ofono_atom *atom)
{
struct ofono_devinfo *info = __ofono_atom_get_data(atom);
g_free(info->manufacturer);
info->manufacturer = NULL;
g_free(info->model);
info->model = NULL;
g_free(info->revision);
info->revision = NULL;
g_free(info->serial);
info->serial = NULL;
g_free(info->svn);
info->svn = NULL;
}
void ofono_devinfo_register(struct ofono_devinfo *info)
{
struct ofono_modem *modem = __ofono_atom_get_modem(info->atom);
__ofono_atom_register(info->atom, devinfo_unregister);
info->dun_watch = __ofono_modem_add_atom_watch(modem,
OFONO_ATOM_TYPE_EMULATOR_DUN,
dun_watch, info, NULL);
query_manufacturer(info);
}
void ofono_devinfo_remove(struct ofono_devinfo *info)
{
__ofono_atom_free(info->atom);
}
void ofono_devinfo_set_data(struct ofono_devinfo *info, void *data)
{
info->driver_data = data;
}
void *ofono_devinfo_get_data(struct ofono_devinfo *info)
{
return info->driver_data;
}
struct ofono_modem *ofono_devinfo_get_modem(struct ofono_devinfo *info)
{
return __ofono_atom_get_modem(info->atom);
}
static void unregister_property(gpointer data)
{
struct modem_property *property = data;
DBG("property %p", property);
g_free(property->value);
g_free(property);
}
static int set_modem_property(struct ofono_modem *modem, const char *name,
enum property_type type, const void *value)
{
struct modem_property *property;
DBG("modem %p property %s", modem, name);
if (type != PROPERTY_TYPE_STRING &&
type != PROPERTY_TYPE_INTEGER &&
type != PROPERTY_TYPE_BOOLEAN)
return -EINVAL;
property = g_try_new0(struct modem_property, 1);
if (property == NULL)
return -ENOMEM;
property->type = type;
switch (type) {
case PROPERTY_TYPE_STRING:
property->value = g_strdup((const char *) value);
break;
case PROPERTY_TYPE_INTEGER:
property->value = g_memdup(value, sizeof(int));
break;
case PROPERTY_TYPE_BOOLEAN:
property->value = g_memdup(value, sizeof(ofono_bool_t));
break;
default:
break;
}
g_hash_table_replace(modem->properties, g_strdup(name), property);
return 0;
}
static gboolean get_modem_property(struct ofono_modem *modem, const char *name,
enum property_type type,
void *value)
{
struct modem_property *property;
DBG("modem %p property %s", modem, name);
property = g_hash_table_lookup(modem->properties, name);
if (property == NULL)
return FALSE;
if (property->type != type)
return FALSE;
switch (property->type) {
case PROPERTY_TYPE_STRING:
*((const char **) value) = property->value;
return TRUE;
case PROPERTY_TYPE_INTEGER:
memcpy(value, property->value, sizeof(int));
return TRUE;
case PROPERTY_TYPE_BOOLEAN:
memcpy(value, property->value, sizeof(ofono_bool_t));
return TRUE;
default:
return FALSE;
}
}
int ofono_modem_set_string(struct ofono_modem *modem,
const char *key, const char *value)
{
return set_modem_property(modem, key, PROPERTY_TYPE_STRING, value);
}
int ofono_modem_set_integer(struct ofono_modem *modem,
const char *key, int value)
{
return set_modem_property(modem, key, PROPERTY_TYPE_INTEGER, &value);
}
int ofono_modem_set_boolean(struct ofono_modem *modem,
const char *key, ofono_bool_t value)
{
return set_modem_property(modem, key, PROPERTY_TYPE_BOOLEAN, &value);
}
const char *ofono_modem_get_string(struct ofono_modem *modem, const char *key)
{
const char *value;
if (get_modem_property(modem, key,
PROPERTY_TYPE_STRING, &value) == FALSE)
return NULL;
return value;
}
int ofono_modem_get_integer(struct ofono_modem *modem, const char *key)
{
int value;
if (get_modem_property(modem, key,
PROPERTY_TYPE_INTEGER, &value) == FALSE)
return 0;
return value;
}
ofono_bool_t ofono_modem_get_boolean(struct ofono_modem *modem, const char *key)
{
ofono_bool_t value;
if (get_modem_property(modem, key,
PROPERTY_TYPE_BOOLEAN, &value) == FALSE)
return FALSE;
return value;
}
void ofono_modem_set_powered_timeout_hint(struct ofono_modem *modem,
unsigned int seconds)
{
modem->timeout_hint = seconds;
}
void ofono_modem_set_name(struct ofono_modem *modem, const char *name)
{
if (modem->name)
g_free(modem->name);
modem->name = g_strdup(name);
if (modem->driver) {
DBusConnection *conn = ofono_dbus_get_connection();
ofono_dbus_signal_property_changed(conn, modem->path,
OFONO_MODEM_INTERFACE,
"Name", DBUS_TYPE_STRING,
&modem->name);
}
}
void ofono_modem_set_driver(struct ofono_modem *modem, const char *type)
{
DBG("type: %s", type);
if (modem->driver)
return;
if (strlen(type) > 16)
return;
g_free(modem->driver_type);
modem->driver_type = g_strdup(type);
}
struct ofono_modem *ofono_modem_create(const char *name, const char *type)
{
struct ofono_modem *modem;
char path[128];
DBG("name: %s, type: %s", name, type);
if (strlen(type) > 16)
return NULL;
if (name && strlen(name) > 64)
return NULL;
if (name == NULL)
snprintf(path, sizeof(path), "/%s_%d", type, next_modem_id);
else
snprintf(path, sizeof(path), "/%s", name);
if (!dbus_validate_path(path, NULL))
return NULL;
modem = g_try_new0(struct ofono_modem, 1);
if (modem == NULL)
return modem;
modem->path = g_strdup(path);
modem->driver_type = g_strdup(type);
modem->properties = g_hash_table_new_full(g_str_hash, g_str_equal,
g_free, unregister_property);
modem->timeout_hint = DEFAULT_POWERED_TIMEOUT;
g_modem_list = g_slist_prepend(g_modem_list, modem);
if (name == NULL)
next_modem_id += 1;
return modem;
}
static void sim_watch(struct ofono_atom *atom,
enum ofono_atom_watch_condition cond, void *data)
{
struct ofono_modem *modem = data;
if (cond == OFONO_ATOM_WATCH_CONDITION_UNREGISTERED) {
modem->sim_ready_watch = 0;
return;
}
modem->sim = __ofono_atom_get_data(atom);
modem->sim_ready_watch = ofono_sim_add_state_watch(modem->sim,
sim_state_watch,
modem, NULL);
}
void __ofono_modemwatch_init(void)
{
g_modemwatches = __ofono_watchlist_new(g_free);
}
void __ofono_modemwatch_cleanup(void)
{
__ofono_watchlist_free(g_modemwatches);
}
unsigned int __ofono_modemwatch_add(ofono_modemwatch_cb_t cb, void *user,
ofono_destroy_func destroy)
{
struct ofono_watchlist_item *watch;
if (cb == NULL)
return 0;
watch = g_new0(struct ofono_watchlist_item, 1);
watch->notify = cb;
watch->destroy = destroy;
watch->notify_data = user;
return __ofono_watchlist_add_item(g_modemwatches, watch);
}
gboolean __ofono_modemwatch_remove(unsigned int id)
{
return __ofono_watchlist_remove_item(g_modemwatches, id);
}
static void call_modemwatches(struct ofono_modem *modem, gboolean added)
{
GSList *l;
struct ofono_watchlist_item *watch;
ofono_modemwatch_cb_t notify;
DBG("%p added:%d", modem, added);
for (l = g_modemwatches->items; l; l = l->next) {
watch = l->data;
notify = watch->notify;
notify(modem, added, watch->notify_data);
}
}
static void emit_modem_added(struct ofono_modem *modem)
{
DBusMessage *signal;
DBusMessageIter iter;
DBusMessageIter dict;
const char *path;
DBG("%p", modem);
signal = dbus_message_new_signal(OFONO_MANAGER_PATH,
OFONO_MANAGER_INTERFACE,
"ModemAdded");
if (signal == NULL)
return;
dbus_message_iter_init_append(signal, &iter);
path = modem->path;
dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
OFONO_PROPERTIES_ARRAY_SIGNATURE,
&dict);
__ofono_modem_append_properties(modem, &dict);
dbus_message_iter_close_container(&iter, &dict);
g_dbus_send_message(ofono_dbus_get_connection(), signal);
}
ofono_bool_t ofono_modem_is_registered(struct ofono_modem *modem)
{
if (modem == NULL)
return FALSE;
if (modem->driver == NULL)
return FALSE;
return TRUE;
}
int ofono_modem_register(struct ofono_modem *modem)
{
DBusConnection *conn = ofono_dbus_get_connection();
GSList *l;
DBG("%p", modem);
if (modem == NULL)
return -EINVAL;
if (powering_down == TRUE)
return -EBUSY;
if (modem->driver != NULL)
return -EALREADY;
for (l = g_driver_list; l; l = l->next) {
const struct ofono_modem_driver *drv = l->data;
if (g_strcmp0(drv->name, modem->driver_type))
continue;
if (drv->probe(modem) < 0)
continue;
modem->driver = drv;
break;
}
if (modem->driver == NULL)
return -ENODEV;
if (!g_dbus_register_interface(conn, modem->path,
OFONO_MODEM_INTERFACE,
modem_methods, modem_signals, NULL,
modem, NULL)) {
ofono_error("Modem register failed on path %s", modem->path);
if (modem->driver->remove)
modem->driver->remove(modem);
modem->driver = NULL;
return -EIO;
}
g_free(modem->driver_type);
modem->driver_type = NULL;
modem->atom_watches = __ofono_watchlist_new(g_free);
modem->online_watches = __ofono_watchlist_new(g_free);
modem->powered_watches = __ofono_watchlist_new(g_free);
emit_modem_added(modem);
call_modemwatches(modem, TRUE);
modem->sim_watch = __ofono_modem_add_atom_watch(modem,
OFONO_ATOM_TYPE_SIM,
sim_watch, modem, NULL);
return 0;
}
static void emit_modem_removed(struct ofono_modem *modem)
{
DBusConnection *conn = ofono_dbus_get_connection();
const char *path = modem->path;
DBG("%p", modem);
g_dbus_emit_signal(conn, OFONO_MANAGER_PATH, OFONO_MANAGER_INTERFACE,
"ModemRemoved", DBUS_TYPE_OBJECT_PATH, &path,
DBUS_TYPE_INVALID);
}
static void modem_unregister(struct ofono_modem *modem)
{
DBusConnection *conn = ofono_dbus_get_connection();
DBG("%p", modem);
if (modem->powered == TRUE)
set_powered(modem, FALSE);
__ofono_watchlist_free(modem->atom_watches);
modem->atom_watches = NULL;
__ofono_watchlist_free(modem->online_watches);
modem->online_watches = NULL;
__ofono_watchlist_free(modem->powered_watches);
modem->powered_watches = NULL;
modem->sim_watch = 0;
modem->sim_ready_watch = 0;
g_slist_free_full(modem->interface_list, g_free);
modem->interface_list = NULL;
g_slist_free_full(modem->feature_list, g_free);
modem->feature_list = NULL;
if (modem->timeout) {
g_source_remove(modem->timeout);
modem->timeout = 0;
}
if (modem->pending) {
dbus_message_unref(modem->pending);
modem->pending = NULL;
}
if (modem->interface_update) {
g_source_remove(modem->interface_update);
modem->interface_update = 0;
}
if (modem->lock_watch) {
lockdown_remove(modem);
ofono_dbus_signal_property_changed(conn, modem->path,
OFONO_MODEM_INTERFACE,
"Lockdown", DBUS_TYPE_BOOLEAN,
&modem->lockdown);
}
g_dbus_unregister_interface(conn, modem->path, OFONO_MODEM_INTERFACE);
if (modem->driver && modem->driver->remove)
modem->driver->remove(modem);
g_hash_table_destroy(modem->properties);
modem->properties = NULL;
modem->driver = NULL;
emit_modem_removed(modem);
call_modemwatches(modem, FALSE);
}
void ofono_modem_remove(struct ofono_modem *modem)
{
DBG("%p", modem);
if (modem == NULL)
return;
if (modem->driver)
modem_unregister(modem);
g_modem_list = g_slist_remove(g_modem_list, modem);
g_free(modem->driver_type);
g_free(modem->name);
g_free(modem->path);
g_free(modem);
}
void ofono_modem_reset(struct ofono_modem *modem)
{
int err;
DBG("%p", modem);
if (modem->pending) {
DBusMessage *reply = __ofono_error_failed(modem->pending);
__ofono_dbus_pending_reply(&modem->pending, reply);
}
if (modem->modem_state == MODEM_STATE_ONLINE)
modem->get_online = TRUE;
ofono_modem_set_powered(modem, FALSE);
err = set_powered(modem, TRUE);
if (err == -EINPROGRESS)
return;
if (err < 0)
return;
modem_change_state(modem, MODEM_STATE_PRE_SIM);
}
void __ofono_modem_sim_reset(struct ofono_modem *modem)
{
DBG("%p", modem);
modem_change_state(modem, MODEM_STATE_PRE_SIM);
}
int ofono_modem_driver_register(const struct ofono_modem_driver *d)
{
DBG("driver: %p, name: %s", d, d->name);
if (d->probe == NULL)
return -EINVAL;
g_driver_list = g_slist_prepend(g_driver_list, (void *) d);
return 0;
}
void ofono_modem_driver_unregister(const struct ofono_modem_driver *d)
{
GSList *l;
struct ofono_modem *modem;
DBG("driver: %p, name: %s", d, d->name);
g_driver_list = g_slist_remove(g_driver_list, (void *) d);
for (l = g_modem_list; l; l = l->next) {
modem = l->data;
if (modem->driver != d)
continue;
modem_unregister(modem);
}
}
void __ofono_modem_shutdown(void)
{
struct ofono_modem *modem;
GSList *l;
powering_down = TRUE;
for (l = g_modem_list; l; l = l->next) {
modem = l->data;
if (modem->driver == NULL)
continue;
if (modem->powered == FALSE && modem->powered_pending == FALSE)
continue;
if (set_powered(modem, FALSE) == -EINPROGRESS)
modems_remaining += 1;
}
if (modems_remaining == 0)
__ofono_exit();
}
void __ofono_modem_foreach(ofono_modem_foreach_func func, void *userdata)
{
struct ofono_modem *modem;
GSList *l;
for (l = g_modem_list; l; l = l->next) {
modem = l->data;
func(modem, userdata);
}
}
struct ofono_modem *ofono_modem_find(ofono_modem_compare_cb_t func,
void *user_data)
{
struct ofono_modem *modem;
GSList *l;
for (l = g_modem_list; l; l = l->next) {
modem = l->data;
if (func(modem, user_data) == TRUE)
return modem;
}
return NULL;
}
ofono_bool_t ofono_modem_get_emergency_mode(struct ofono_modem *modem)
{
return modem->emergency != 0;
}
void __ofono_modem_inc_emergency_mode(struct ofono_modem *modem)
{
DBusConnection *conn = ofono_dbus_get_connection();
dbus_bool_t emergency = TRUE;
if (++modem->emergency > 1)
return;
ofono_dbus_signal_property_changed(conn, modem->path,
OFONO_MODEM_INTERFACE,
"Emergency", DBUS_TYPE_BOOLEAN,
&emergency);
}
void __ofono_modem_dec_emergency_mode(struct ofono_modem *modem)
{
DBusConnection *conn = ofono_dbus_get_connection();
dbus_bool_t emergency = FALSE;
if (modem->emergency == 0) {
ofono_error("emergency mode is already deactivated!!!");
return;
}
if (modem->emergency > 1)
goto out;
ofono_dbus_signal_property_changed(conn, modem->path,
OFONO_MODEM_INTERFACE,
"Emergency", DBUS_TYPE_BOOLEAN,
&emergency);
out:
modem->emergency--;
}