/* * * 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 #endif #include #include #include #include #include #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); }