diff options
Diffstat (limited to 'src/network.c')
-rw-r--r-- | src/network.c | 1062 |
1 files changed, 1062 insertions, 0 deletions
diff --git a/src/network.c b/src/network.c new file mode 100644 index 00000000..bebc0c15 --- /dev/null +++ b/src/network.c @@ -0,0 +1,1062 @@ +/* + * + * oFono - Open Source Telephony + * + * Copyright (C) 2008-2009 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 <dbus/dbus.h> +#include <glib.h> +#include <gdbus.h> + +#include "ofono.h" + +#include "dbus-gsm.h" +#include "modem.h" +#include "driver.h" +#include "common.h" + +#define NETWORK_REGISTRATION_INTERFACE "org.ofono.NetworkRegistration" +#define NETWORK_OPERATOR_INTERFACE "org.ofono.NetworkOperator" + +#define NETWORK_REGISTRATION_FLAG_REQUESTING_OPLIST 0x1 +#define NETWORK_REGISTRATION_FLAG_PENDING 0x2 + +#define AUTO_REGISTER 1 + +/* How often we update the operator list, in seconds */ +#define OPERATOR_LIST_UPDATE_TIME 300 + +struct network_registration_data { + int status; + int location; + int cellid; + int technology; + struct ofono_network_operator *current_operator; + GSList *operator_list; + struct ofono_network_registration_ops *ops; + int flags; + DBusMessage *pending; + int signal_strength; +}; + +static void operator_list_callback(const struct ofono_error *error, int total, + const struct ofono_network_operator *list, + void *data); + +static void current_operator_callback(const struct ofono_error *error, + const struct ofono_network_operator *current, + void *data); + +static void signal_strength_callback(const struct ofono_error *error, + int strength, void *data); + +static void registration_status_callback(const struct ofono_error *error, + int status, int lac, int ci, int tech, + void *data); + +struct ofono_network_operator_data { + struct ofono_network_operator *operator; + struct ofono_modem *modem; +}; + +static inline const char *network_operator_status_to_string(int status) +{ + switch (status) { + case OPERATOR_STATUS_AVAILABLE: + return "available"; + case OPERATOR_STATUS_CURRENT: + return "current"; + case OPERATOR_STATUS_FORBIDDEN: + return "forbidden"; + } + + return "unknown"; +} + +static inline const char *registration_status_to_string(int status) +{ + switch (status) { + case NETWORK_REGISTRATION_STATUS_NOT_REGISTERED: + return "unregistered"; + case NETWORK_REGISTRATION_STATUS_REGISTERED: + return "registered"; + case NETWORK_REGISTRATION_STATUS_SEARCHING: + return "searching"; + case NETWORK_REGISTRATION_STATUS_DENIED: + return "denied"; + case NETWORK_REGISTRATION_STATUS_UNKNOWN: + return "unknown"; + case NETWORK_REGISTRATION_STATUS_ROAMING: + return "roaming"; + } + + return ""; +} + +static inline const char *registration_tech_to_string(int tech) +{ + switch (tech) { + case ACCESS_TECHNOLOGY_GSM: + return "GSM"; + case ACCESS_TECHNOLOGY_GSM_COMPACT: + return "GSMCompact"; + case ACCESS_TECHNOLOGY_UTRAN: + return "UTRAN"; + case ACCESS_TECHNOLOGY_GSM_EGPRS: + return "GSM+EGPS"; + case ACCESS_TECHNOLOGY_UTRAN_HSDPA: + return "UTRAN+HSDPA"; + case ACCESS_TECHNOLOGY_UTRAN_HSUPA: + return "UTRAN+HSUPA"; + case ACCESS_TECHNOLOGY_UTRAN_HSDPA_HSUPA: + return "UTRAN+HSDPA+HSUPA"; + default: + return ""; + } +} + +static void register_callback(const struct ofono_error *error, void *data) +{ + struct ofono_modem *modem = data; + struct network_registration_data *netreg = modem->network_registration; + DBusConnection *conn = dbus_gsm_connection(); + DBusMessage *reply; + + if (!netreg->pending) + goto out; + + if (error->type == OFONO_ERROR_TYPE_NO_ERROR) + reply = dbus_message_new_method_return(netreg->pending); + else + reply = dbus_gsm_failed(netreg->pending); + + g_dbus_send_message(conn, reply); + + dbus_message_unref(netreg->pending); + netreg->pending = NULL; + +out: + netreg->flags &= ~NETWORK_REGISTRATION_FLAG_PENDING; + + if (netreg->ops->registration_status) + netreg->ops->registration_status(modem, + registration_status_callback, modem); +} + +/* Must use dbus_gsm_free_string_array on network_operators */ +static void network_operator_populate_registered(struct ofono_modem *modem, + char ***network_operators) +{ + DBusConnection *conn = dbus_gsm_connection(); + char **children; + int i; + int modem_len; + int num_children; + GSList *l; + int *mccmnc; + char path[MAX_DBUS_PATH_LEN]; + + modem_len = snprintf(path, MAX_DBUS_PATH_LEN, "%s/operator", + modem->path); + + if (!dbus_connection_list_registered(conn, path, &children)) { + ofono_debug("Unable to obtain registered NetworkOperator(s)"); + *network_operators = g_try_new0(char *, 1); + return; + } + + for (i = 0; children[i]; i++) + ; + + num_children = i; + + *network_operators = g_try_new0(char *, num_children + 1); + + mccmnc = g_try_new0(int, num_children * 2); + for (i = 0; i < num_children; i++) + sscanf(children[i], "%3d%3d", &mccmnc[i*2], &mccmnc[i*2+1]); + + /* Quoting 27.007: "The list of operators shall be in order: home + * network, networks referenced in SIM or active application in the + * UICC (GSM or USIM) in the following order: HPLMN selector, User + * controlled PLMN selector, Operator controlled PLMN selector and + * PLMN selector (in the SIM or GSM application), and other networks." + * Thus we must make sure we return the list in the same order, + * if possible. Luckily the operator_list is stored in order already + */ + i = 0; + for (l = modem->network_registration->operator_list; l; l = l->next) { + struct ofono_network_operator *op = l->data; + int j; + + for (j = 0; children[j]; j++) { + if (op->mcc == mccmnc[j*2] && op->mnc == mccmnc[j*2+1]) { + /* Enough to store '/' + 3 char wide MCC + 3 char wide MNC + null */ + (*network_operators)[i] = g_try_new(char, modem_len + 8); + snprintf((*network_operators)[i], modem_len + 8, "%s/%s", + path, children[j]); + ++i; + } + } + } + + g_free(mccmnc); + + dbus_free_string_array(children); +} + +static void network_operator_destroy(gpointer userdata) +{ + struct ofono_network_operator_data *op = userdata; + + g_free(op); +} + +static gint network_operator_compare(gconstpointer a, gconstpointer b) +{ + const struct ofono_network_operator *opa = a; + const struct ofono_network_operator *opb = b; + + if (opa->mcc < opb->mcc) + return -1; + + if (opa->mcc > opb->mcc) + return 1; + + if (opa->mnc < opb->mnc) + return -1; + + if (opa->mnc > opb->mnc) + return 1; + + return 0; +} + +static inline const char *network_operator_build_path(struct ofono_modem *modem, + struct ofono_network_operator *oper) +{ + static char path[MAX_DBUS_PATH_LEN]; + + snprintf(path, MAX_DBUS_PATH_LEN, "%s/operator/%03d%03d", + modem->path, oper->mcc, oper->mnc); + + return path; +} + +static void network_operator_emit_available_operators(struct ofono_modem *modem) +{ + //struct network_registration_data *netreg = modem->network_registration; + DBusConnection *conn = dbus_gsm_connection(); + char **network_operators; + + network_operator_populate_registered(modem, &network_operators); + + dbus_gsm_signal_array_property_changed(conn, modem->path, + NETWORK_REGISTRATION_INTERFACE, + "AvailableOperators", + DBUS_TYPE_OBJECT_PATH, + &network_operators); + + dbus_gsm_free_string_array(network_operators); +} + +static void set_network_operator_status(struct ofono_modem *modem, + struct ofono_network_operator *op, + int status) +{ + DBusConnection *conn = dbus_gsm_connection(); + //struct network_registration_data *netreg = modem->network_registration; + const char *status_str; + const char *path; + + if (op->status == status) + return; + + op->status = status; + + status_str = network_operator_status_to_string(status); + path = network_operator_build_path(modem, op); + + dbus_gsm_signal_property_changed(conn, path, NETWORK_OPERATOR_INTERFACE, + "Status", DBUS_TYPE_STRING, + &status_str); +} + +static void set_network_operator_technology(struct ofono_modem *modem, + struct ofono_network_operator *op, + int tech) +{ + //struct network_registration_data *netreg = modem->network_registration; + DBusConnection *conn = dbus_gsm_connection(); + const char *tech_str; + const char *path; + + if (op->tech == tech) + return; + + op->tech = tech; + tech_str = registration_tech_to_string(tech); + path = network_operator_build_path(modem, op); + + dbus_gsm_signal_property_changed(conn, path, NETWORK_OPERATOR_INTERFACE, + "Technology", DBUS_TYPE_STRING, + &tech_str); +} + +static void set_network_operator_name(struct ofono_modem *modem, + struct ofono_network_operator *op, + const char *name) +{ + struct network_registration_data *netreg = modem->network_registration; + DBusConnection *conn = dbus_gsm_connection(); + const char *path; + + if (!strncmp(op->name, name, OFONO_MAX_OPERATOR_NAME_LENGTH)) + return; + + strncpy(op->name, name, OFONO_MAX_OPERATOR_NAME_LENGTH); + op->name[OFONO_MAX_OPERATOR_NAME_LENGTH] = '\0'; + + path = network_operator_build_path(modem, op); + + dbus_gsm_signal_property_changed(conn, path, + NETWORK_OPERATOR_INTERFACE, + "Name", DBUS_TYPE_STRING, + &name); + + if (op == netreg->current_operator) + dbus_gsm_signal_property_changed(conn, modem->path, + NETWORK_REGISTRATION_INTERFACE, + "Operator", DBUS_TYPE_STRING, + &name); +} + +static DBusMessage *network_operator_get_properties(DBusConnection *conn, + DBusMessage *msg, + void *data) +{ + struct ofono_network_operator_data *op = data; + DBusMessage *reply; + DBusMessageIter iter; + DBusMessageIter dict; + + const char *name = op->operator->name; + const char *status = + network_operator_status_to_string(op->operator->status); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return NULL; + + dbus_message_iter_init_append(reply, &iter); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + PROPERTIES_ARRAY_SIGNATURE, + &dict); + + dbus_gsm_dict_append(&dict, "Name", DBUS_TYPE_STRING, &name); + + dbus_gsm_dict_append(&dict, "Status", DBUS_TYPE_STRING, &status); + + if (op->operator->mcc != -1) { + dbus_uint16_t mcc = op->operator->mcc; + dbus_gsm_dict_append(&dict, "MobileCountryCode", + DBUS_TYPE_UINT16, &mcc); + } + + if (op->operator->mnc != -1) { + dbus_uint16_t mnc = op->operator->mnc; + dbus_gsm_dict_append(&dict, "MobileNetworkCode", + DBUS_TYPE_UINT16, &mnc); + } + + if (op->operator->tech != -1) { + const char *technology = + registration_tech_to_string(op->operator->tech); + + dbus_gsm_dict_append(&dict, "Technology", DBUS_TYPE_STRING, + &technology); + } + + dbus_message_iter_close_container(&iter, &dict); + + return reply; +} + +static DBusMessage *network_operator_register(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct ofono_network_operator_data *op = data; + struct network_registration_data *netreg = op->modem->network_registration; + + if (netreg->flags & NETWORK_REGISTRATION_FLAG_PENDING) + return dbus_gsm_busy(msg); + + if (netreg->ops->register_manual == NULL) + return dbus_gsm_not_implemented(msg); + + netreg->flags |= NETWORK_REGISTRATION_FLAG_PENDING; + netreg->pending = dbus_message_ref(msg); + + netreg->ops->register_manual(op->modem, op->operator, + register_callback, op->modem); + + return NULL; +} + +static GDBusMethodTable network_operator_methods[] = { + { "GetProperties", "", "a{sv}", network_operator_get_properties }, + { "Register", "", "", network_operator_register, + G_DBUS_METHOD_FLAG_ASYNC }, + { } +}; + +static GDBusSignalTable network_operator_signals[] = { + { "PropertyChanged", "sv" }, + { } +}; + +static gboolean network_operator_dbus_register(struct ofono_modem *modem, + struct ofono_network_operator *op) +{ + DBusConnection *conn = dbus_gsm_connection(); + const char *path; + + struct ofono_network_operator_data *opd = + g_try_new(struct ofono_network_operator_data, 1); + + if (!opd) + return FALSE; + + opd->operator = op; + opd->modem = modem; + + path = network_operator_build_path(modem, op); + + if (!g_dbus_register_interface(conn, path, NETWORK_OPERATOR_INTERFACE, + network_operator_methods, + network_operator_signals, + NULL, opd, + network_operator_destroy)) { + ofono_error("Could not register NetworkOperator %s", path); + network_operator_destroy(opd); + + return FALSE; + } + + return TRUE; +} + +static gboolean network_operator_dbus_unregister(struct ofono_modem *modem, + struct ofono_network_operator *op) +{ + DBusConnection *conn = dbus_gsm_connection(); + const char *path = network_operator_build_path(modem, op); + + return g_dbus_unregister_interface(conn, path, + NETWORK_OPERATOR_INTERFACE); +} + +static struct network_registration_data *network_registration_create() +{ + struct network_registration_data *data; + + data = g_try_new0(struct network_registration_data, 1); + if (data == NULL) + return data; + + data->status = NETWORK_REGISTRATION_STATUS_UNKNOWN; + data->location = -1; + data->cellid = -1; + data->technology = -1; + data->signal_strength = -1; + + return data; +} + +static void network_registration_destroy(gpointer userdata) +{ + struct ofono_modem *modem = userdata; + struct network_registration_data *data = modem->network_registration; + GSList *l; + + for (l = data->operator_list; l; l = l->next) { + network_operator_dbus_unregister(modem, l->data); + g_free(l->data); + } + + g_slist_free(data->operator_list); + + g_free(data); + + modem->network_registration = 0; +} + +static DBusMessage *network_get_properties(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct ofono_modem *modem = data; + struct network_registration_data *netreg = modem->network_registration; + DBusMessage *reply; + DBusMessageIter iter; + DBusMessageIter dict; + + const char *status = registration_status_to_string(netreg->status); + const char *operator = + netreg->current_operator ? netreg->current_operator->name : ""; + + char **network_operators; + + reply = dbus_message_new_method_return(msg); + if (!reply) + return NULL; + + dbus_message_iter_init_append(reply, &iter); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + PROPERTIES_ARRAY_SIGNATURE, + &dict); + + dbus_gsm_dict_append(&dict, "Status", DBUS_TYPE_STRING, &status); + + if (netreg->location != -1) { + dbus_uint16_t location = netreg->location; + dbus_gsm_dict_append(&dict, "LocationAreaCode", + DBUS_TYPE_UINT16, &location); + } + + if (netreg->cellid != -1) { + dbus_uint32_t cellid = netreg->cellid; + dbus_gsm_dict_append(&dict, "CellId", + DBUS_TYPE_UINT32, &cellid); + } + + if (netreg->technology != -1) { + const char *technology = + registration_tech_to_string(netreg->technology); + + dbus_gsm_dict_append(&dict, "Technology", DBUS_TYPE_STRING, + &technology); + } + + dbus_gsm_dict_append(&dict, "Operator", DBUS_TYPE_STRING, &operator); + + network_operator_populate_registered(modem, &network_operators); + + dbus_gsm_dict_append_array(&dict, "AvailableOperators", + DBUS_TYPE_OBJECT_PATH, + &network_operators); + + dbus_gsm_free_string_array(network_operators); + + if (netreg->signal_strength != -1) { + dbus_uint16_t strength = netreg->signal_strength; + dbus_gsm_dict_append(&dict, "Strength", DBUS_TYPE_UINT16, + &strength); + } + + dbus_message_iter_close_container(&iter, &dict); + + return reply; +} + +static DBusMessage *network_register(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct ofono_modem *modem = data; + struct network_registration_data *netreg = modem->network_registration; + + if (netreg->flags & NETWORK_REGISTRATION_FLAG_PENDING) + return dbus_gsm_busy(msg); + + if (netreg->ops->register_auto == NULL) + return dbus_gsm_not_implemented(msg); + + netreg->flags |= NETWORK_REGISTRATION_FLAG_PENDING; + netreg->pending = dbus_message_ref(msg); + + netreg->ops->register_auto(modem, register_callback, modem); + + return NULL; +} + +static DBusMessage *network_deregister(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct ofono_modem *modem = data; + struct network_registration_data *netreg = modem->network_registration; + + if (netreg->flags & NETWORK_REGISTRATION_FLAG_PENDING) + return dbus_gsm_busy(msg); + + if (netreg->ops->deregister == NULL) + return dbus_gsm_not_implemented(msg); + + netreg->flags |= NETWORK_REGISTRATION_FLAG_PENDING; + netreg->pending = dbus_message_ref(msg); + + netreg->ops->deregister(modem, register_callback, modem); + + return NULL; +} + +static GDBusMethodTable network_registration_methods[] = { + { "GetProperties", "", "a{sv}", network_get_properties }, + { "Register", "", "", network_register, + G_DBUS_METHOD_FLAG_ASYNC }, + { "Deregister", "", "", network_deregister, + G_DBUS_METHOD_FLAG_ASYNC }, + { } +}; + +static GDBusSignalTable network_registration_signals[] = { + { "PropertyChanged", "sv" }, + { } +}; + +static void update_network_operator_list(struct ofono_modem *modem) +{ + struct network_registration_data *netreg = modem->network_registration; + + if (netreg->flags & NETWORK_REGISTRATION_FLAG_REQUESTING_OPLIST) + return; + + if (!netreg->ops->list_operators) + return; + + netreg->flags |= NETWORK_REGISTRATION_FLAG_REQUESTING_OPLIST; + netreg->ops->list_operators(modem, operator_list_callback, modem); +} + +static gboolean update_network_operator_list_cb(void *user_data) +{ + struct ofono_modem *modem = user_data; + + update_network_operator_list(modem); + + return TRUE; +} + +static gboolean update_network_operator_list_init(void *user_data) +{ + struct ofono_modem *modem = user_data; + + update_network_operator_list(modem); + + return FALSE; +} + +static void set_registration_status(struct ofono_modem *modem, int status) +{ + const char *str_status = registration_status_to_string(status); + struct network_registration_data *netreg = modem->network_registration; + DBusConnection *conn = dbus_gsm_connection(); + + netreg->status = status; + + dbus_gsm_signal_property_changed(conn, modem->path, + NETWORK_REGISTRATION_INTERFACE, + "Status", DBUS_TYPE_STRING, + &str_status); +} + +static void set_registration_location(struct ofono_modem *modem, int lac) +{ + struct network_registration_data *netreg = modem->network_registration; + DBusConnection *conn = dbus_gsm_connection(); + dbus_uint16_t dbus_lac = lac; + + if (lac > 0xffff) + return; + + netreg->location = lac; + + if (netreg->location == -1) + return; + + dbus_gsm_signal_property_changed(conn, modem->path, + NETWORK_REGISTRATION_INTERFACE, + "LocationAreaCode", + DBUS_TYPE_UINT16, &dbus_lac); +} + +static void set_registration_cellid(struct ofono_modem *modem, int ci) +{ + struct network_registration_data *netreg = modem->network_registration; + DBusConnection *conn = dbus_gsm_connection(); + dbus_uint16_t dbus_ci = ci; + + netreg->cellid = ci; + + if (netreg->cellid == -1) + return; + + dbus_gsm_signal_property_changed(conn, modem->path, + NETWORK_REGISTRATION_INTERFACE, + "CellId", DBUS_TYPE_UINT32, + &dbus_ci); +} + +static void set_registration_technology(struct ofono_modem *modem, int tech) +{ + struct network_registration_data *netreg = modem->network_registration; + const char *tech_str = registration_tech_to_string(tech); + DBusConnection *conn = dbus_gsm_connection(); + + netreg->technology = tech; + + if (netreg->technology == -1) + return; + + dbus_gsm_signal_property_changed(conn, modem->path, + NETWORK_REGISTRATION_INTERFACE, + "Technology", DBUS_TYPE_STRING, + &tech_str); +} + +static void initialize_network_registration(struct ofono_modem *modem) +{ + DBusConnection *conn = dbus_gsm_connection(); + + if (!g_dbus_register_interface(conn, modem->path, + NETWORK_REGISTRATION_INTERFACE, + network_registration_methods, + network_registration_signals, + NULL, modem, + network_registration_destroy)) { + ofono_error("Could not register NetworkRegistration interface"); + network_registration_destroy(modem); + + return; + } + + ofono_debug("NetworkRegistration interface for modem: %s created", + modem->path); + + modem_add_interface(modem, NETWORK_REGISTRATION_INTERFACE); + + if (modem->network_registration->ops->list_operators) { + g_timeout_add_seconds(OPERATOR_LIST_UPDATE_TIME, + update_network_operator_list_cb, modem); + + g_timeout_add_seconds(5, update_network_operator_list_init, + modem); + } +} + +void ofono_network_registration_notify(struct ofono_modem *modem, int status, + int lac, int ci, int tech) +{ + struct network_registration_data *netreg = modem->network_registration; + + if (!netreg) + return; + + if (netreg->status != status) + set_registration_status(modem, status); + + if (netreg->location != lac) + set_registration_location(modem, lac); + + if (netreg->cellid != ci) + set_registration_cellid(modem, ci); + + if (netreg->technology != tech) + set_registration_technology(modem, tech); + + if (netreg->status == 1 || netreg->status == 5) { + if (netreg->ops->current_operator) + netreg->ops->current_operator(modem, + current_operator_callback, modem); + } else { + struct ofono_error error; + + error.type = OFONO_ERROR_TYPE_NO_ERROR; + error.error = 0; + + current_operator_callback(&error, NULL, modem); + + netreg->signal_strength = -1; + } +} + +static void operator_list_callback(const struct ofono_error *error, int total, + const struct ofono_network_operator *list, + void *data) +{ + struct ofono_modem *modem = data; + struct network_registration_data *netreg = modem->network_registration; + GSList *n = NULL; + GSList *o; + int i; + gboolean need_to_emit = FALSE; + + netreg->flags &= ~NETWORK_REGISTRATION_FLAG_REQUESTING_OPLIST; + + if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { + ofono_debug("Error occurred during operator list"); + return; + } + + for (i = 0; i < total; i++) { + o = g_slist_find_custom(netreg->operator_list, &list[i], + network_operator_compare); + + if (o) { /* Update and move to a new list */ + set_network_operator_status(modem, o->data, + list[i].status); + + set_network_operator_technology(modem, o->data, + list[i].tech); + + set_network_operator_name(modem, o->data, + list[i].name); + + n = g_slist_prepend(n, o->data); + netreg->operator_list = + g_slist_remove(netreg->operator_list, o->data); + } else { + /* New operator */ + struct ofono_network_operator *op = + g_try_new0(struct ofono_network_operator, 1); + if (!op) + continue; + + memcpy(op, &list[i], sizeof(struct ofono_network_operator)); + + n = g_slist_prepend(n, op); + + network_operator_dbus_register(modem, op); + + need_to_emit = TRUE; + } + } + + if (n) + n = g_slist_reverse(n); + + if (netreg->operator_list) + need_to_emit = TRUE; + + for (o = netreg->operator_list; o; o = o->next) { + network_operator_dbus_unregister(modem, o->data); + g_free(o->data); + } + + g_slist_free(netreg->operator_list); + + netreg->operator_list = n; + + if (need_to_emit) + network_operator_emit_available_operators(modem); +} + +static void current_operator_callback(const struct ofono_error *error, + const struct ofono_network_operator *current, + void *data) +{ + DBusConnection *conn = dbus_gsm_connection(); + struct ofono_modem *modem = data; + struct network_registration_data *netreg = modem->network_registration; + GSList *op = NULL; + const char *operator; + + if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { + ofono_debug("Error during current operator"); + return; + } + + if (!netreg->current_operator && !current) + return; + + /* We got a new network operator, reset the previous one's status */ + /* It will be updated properly later */ + if (netreg->current_operator && + (!current || + network_operator_compare(current, netreg->current_operator))) + set_network_operator_status(modem, netreg->current_operator, + OPERATOR_STATUS_AVAILABLE); + + if (current) + op = g_slist_find_custom(netreg->operator_list, current, + network_operator_compare); + + if (op) { + netreg->current_operator = op->data; + set_network_operator_status(modem, op->data, + OPERATOR_STATUS_CURRENT); + set_network_operator_technology(modem, op->data, + current->tech); + set_network_operator_name(modem, op->data, current->name); + + return; + } + + if (current) { + netreg->current_operator = + g_try_new0(struct ofono_network_operator, 1); + + if (!netreg->current_operator) { + ofono_error("Unable to allocate current operator"); + return; + } + + memcpy(netreg->current_operator, current, + sizeof(struct ofono_network_operator)); + + netreg->operator_list = g_slist_append(netreg->operator_list, + netreg->current_operator); + + network_operator_dbus_register(modem, netreg->current_operator); + network_operator_emit_available_operators(modem); + } else { + /* We don't free this here because operator is registered */ + /* Taken care of elsewhere */ + netreg->current_operator = NULL; + } + + operator = + netreg->current_operator ? netreg->current_operator->name : ""; + + dbus_gsm_signal_property_changed(conn, modem->path, + NETWORK_REGISTRATION_INTERFACE, + "Operator", DBUS_TYPE_STRING, + &operator); +} + +static void registration_status_callback(const struct ofono_error *error, + int status, int lac, int ci, int tech, + void *data) +{ + struct ofono_modem *modem = data; + + if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { + ofono_debug("Error during registration status query"); + return; + } + + ofono_network_registration_notify(modem, status, lac, ci, tech); +} + +static void init_registration_status(const struct ofono_error *error, + int status, int lac, int ci, int tech, + void *data) +{ + struct ofono_modem *modem = data; + struct network_registration_data *netreg = modem->network_registration; + + if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { + ofono_debug("Error during registration status query"); + return; + } + + ofono_network_registration_notify(modem, status, lac, ci, tech); + + /* Bootstrap our signal strength value without waiting for the + * stack to report it + */ + if (netreg->status == 1 || netreg->status == 5) { + if (netreg->ops->signal_strength) + netreg->ops->signal_strength(modem, + signal_strength_callback, modem); + } + + if (AUTO_REGISTER && (status == 0 || status == 3)) + netreg->ops->register_auto(modem, register_callback, modem); +} + +void ofono_signal_strength_notify(struct ofono_modem *modem, int strength) +{ + struct network_registration_data *netreg = modem->network_registration; + DBusConnection *conn = dbus_gsm_connection(); + + if (netreg->signal_strength == strength) + return; + + /* Theoretically we can get signal strength even when not registered + * to any network. However, what do we do with it in that case? + */ + if (netreg->status != NETWORK_REGISTRATION_STATUS_REGISTERED && + netreg->status != NETWORK_REGISTRATION_STATUS_ROAMING) + return; + + netreg->signal_strength = strength; + + if (strength != -1) { + dbus_uint16_t strength = netreg->signal_strength; + + dbus_gsm_signal_property_changed(conn, modem->path, + NETWORK_REGISTRATION_INTERFACE, + "Strength", DBUS_TYPE_UINT16, + &strength); + } +} + +static void signal_strength_callback(const struct ofono_error *error, + int strength, void *data) +{ + struct ofono_modem *modem = data; + + if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { + ofono_debug("Error during signal strength query"); + return; + } + + ofono_signal_strength_notify(modem, strength); +} + +int ofono_network_registration_register(struct ofono_modem *modem, + struct ofono_network_registration_ops *ops) +{ + if (modem == NULL) + return -1; + + if (ops == NULL) + return -1; + + modem->network_registration = network_registration_create(); + if (modem->network_registration == NULL) + return -1; + + modem->network_registration->ops = ops; + + initialize_network_registration(modem); + + if (ops->registration_status) + ops->registration_status(modem, init_registration_status, + modem); + + return 0; +} + +void ofono_network_registration_unregister(struct ofono_modem *modem) +{ + DBusConnection *conn = dbus_gsm_connection(); + + g_dbus_unregister_interface(conn, modem->path, + NETWORK_REGISTRATION_INTERFACE); + modem_remove_interface(modem, NETWORK_REGISTRATION_INTERFACE); +} + |