From dd361db9c438cc56a2141615aa53b633ea3321c1 Mon Sep 17 00:00:00 2001 From: Marcel Holtmann Date: Wed, 5 Jan 2011 22:02:08 -0800 Subject: [PATCH] tools: Add utility for auto enabling modems --- .gitignore | 1 + Makefile.am | 7 +- tools/auto-enable.c | 494 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 500 insertions(+), 2 deletions(-) create mode 100644 tools/auto-enable.c diff --git a/.gitignore b/.gitignore index e78ce807..7cfb1d95 100644 --- a/.gitignore +++ b/.gitignore @@ -41,6 +41,7 @@ unit/test-caif unit/test-stkutil tools/huawei-audio +tools/auto-enable gatchat/gsmdial gatchat/test-server diff --git a/Makefile.am b/Makefile.am index 5028f4f4..09dc9ad3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -492,15 +492,18 @@ unit_objects += $(unit_test_mux_OBJECTS) unit_test_caif_SOURCES = unit/test-caif.c $(gatchat_sources) \ drivers/stemodem/caif_socket.h \ - drivers/stemodem/if_caif.h + drivers/stemodem/if_caif.h unit_test_caif_LDADD = @GLIB_LIBS@ unit_objects += $(unit_test_caif_OBJECTS) -noinst_PROGRAMS += tools/huawei-audio +noinst_PROGRAMS += tools/huawei-audio tools/auto-enable tools_huawei_audio_SOURCES = $(gdbus_sources) tools/huawei-audio.c tools_huawei_audio_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ +tools_auto_enable_SOURCES = $(gdbus_sources) tools/auto-enable.c +tools_auto_enable_LDADD = @GLIB_LIBS@ @DBUS_LIBS@ + noinst_PROGRAMS += gatchat/gsmdial gatchat/test-server gatchat/test-qcdm gatchat_gsmdial_SOURCES = gatchat/gsmdial.c $(gatchat_sources) diff --git a/tools/auto-enable.c b/tools/auto-enable.c new file mode 100644 index 00000000..560e8f5d --- /dev/null +++ b/tools/auto-enable.c @@ -0,0 +1,494 @@ +/* + * + * oFono - Open Source Telephony + * + * Copyright (C) 2008-2010 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 +#include + +#include + +#define OFONO_SERVICE "org.ofono" + +#define OFONO_MANAGER_INTERFACE OFONO_SERVICE ".Manager" +#define OFONO_MODEM_INTERFACE OFONO_SERVICE ".Modem" + +struct modem_data { + char *path; + DBusConnection *conn; + dbus_bool_t has_powered; + dbus_bool_t has_online; +}; + +static GHashTable *modem_list; + +static void set_property_reply(DBusPendingCall *call, void *user_data) +{ + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusError err; + + dbus_error_init(&err); + + if (dbus_set_error_from_message(&err, reply) == TRUE) { + g_printerr("%s: %s\n", err.name, err.message); + dbus_error_free(&err); + } + + dbus_message_unref(reply); +} + +static int set_property(struct modem_data *modem, const char *key, + int type, const void *val) +{ + DBusConnection *conn = modem->conn; + DBusMessage *msg; + DBusMessageIter iter, value; + DBusPendingCall *call; + const char *signature; + + msg = dbus_message_new_method_call(OFONO_SERVICE, modem->path, + OFONO_MODEM_INTERFACE, "SetProperty"); + if (msg == NULL) + return -ENOMEM; + + dbus_message_set_auto_start(msg, FALSE); + + dbus_message_iter_init_append(msg, &iter); + + dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &key); + + switch (type) { + case DBUS_TYPE_BOOLEAN: + signature = DBUS_TYPE_BOOLEAN_AS_STRING; + break; + default: + dbus_message_unref(msg); + return -EINVAL; + } + + dbus_message_iter_open_container(&iter, DBUS_TYPE_VARIANT, + signature, &value); + dbus_message_iter_append_basic(&value, type, val); + dbus_message_iter_close_container(&iter, &value); + + if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) { + dbus_message_unref(msg); + return -EIO; + } + + dbus_message_unref(msg); + + if (call == NULL) + return -EINVAL; + + dbus_pending_call_set_notify(call, set_property_reply, modem, NULL); + + dbus_pending_call_unref(call); + + return 0; +} + +static void check_property(struct modem_data *modem, const char *key, + DBusMessageIter *value) +{ + if (g_str_equal(key, "Powered") == TRUE) { + dbus_bool_t powered; + + dbus_message_iter_get_basic(value, &powered); + + if (powered == TRUE) { + g_print("modem enabled (%s)\n", modem->path); + + modem->has_powered = TRUE; + } else { + g_print("modem disabled (%s)\n", modem->path); + + if (modem->has_powered == FALSE) { + powered = TRUE; + + set_property(modem, "Powered", + DBUS_TYPE_BOOLEAN, &powered); + } + } + } else if (g_str_equal(key, "Online") == TRUE) { + dbus_bool_t online; + + dbus_message_iter_get_basic(value, &online); + + if (online == TRUE) { + g_print("modem online (%s)\n", modem->path); + + modem->has_online = TRUE; + } else + g_print("modem offline (%s)\n", modem->path); + } else if (g_str_equal(key, "Lockdown") == TRUE) { + dbus_bool_t lockdown; + + dbus_message_iter_get_basic(value, &lockdown); + + if (lockdown == TRUE) + g_print("modem locked (%s)\n", modem->path); + else + g_print("modem unlocked (%s)\n", modem->path); + } +} + +static void destroy_modem(gpointer data) +{ + struct modem_data *modem = data; + + g_print("modem removed (%s)\n", modem->path); + + dbus_connection_unref(modem->conn); + + g_free(modem->path); + g_free(modem); +} + +static void create_modem(DBusConnection *conn, + const char *path, DBusMessageIter *iter) +{ + struct modem_data *modem; + DBusMessageIter dict; + + modem = g_try_new0(struct modem_data, 1); + if (modem == NULL) + return; + + modem->path = g_strdup(path); + modem->conn = dbus_connection_ref(conn); + + g_hash_table_replace(modem_list, modem->path, modem); + + g_print("modem added (%s)\n", modem->path); + + dbus_message_iter_recurse(iter, &dict); + + while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter entry, value; + const char *key; + + dbus_message_iter_recurse(&dict, &entry); + dbus_message_iter_get_basic(&entry, &key); + + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &value); + + check_property(modem, key, &value); + + dbus_message_iter_next(&dict); + } +} + +static gboolean modem_added(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + DBusMessageIter iter, dict; + const char *path; + + if (dbus_message_iter_init(msg, &iter) == FALSE) + return TRUE; + + dbus_message_iter_get_basic(&iter, &path); + + dbus_message_iter_next(&iter); + dbus_message_iter_recurse(&iter, &dict); + + create_modem(conn, path, &iter); + + return TRUE; +} + +static gboolean modem_removed(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + DBusMessageIter iter; + const char *path; + + if (dbus_message_iter_init(msg, &iter) == FALSE) + return TRUE; + + dbus_message_iter_get_basic(&iter, &path); + + g_hash_table_remove(modem_list, path); + + return TRUE; +} + +static gboolean modem_changed(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + struct modem_data *modem; + DBusMessageIter iter, value; + const char *path, *key; + + if (dbus_message_iter_init(msg, &iter) == FALSE) + return TRUE; + + path = dbus_message_get_path(msg); + + modem = g_hash_table_lookup(modem_list, path); + if (modem == NULL) + return TRUE; + + dbus_message_iter_get_basic(&iter, &key); + + dbus_message_iter_next(&iter); + dbus_message_iter_recurse(&iter, &value); + + check_property(modem, key, &value); + + return TRUE; +} + +static void get_modems_reply(DBusPendingCall *call, void *user_data) +{ + DBusConnection *conn = user_data; + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusMessageIter iter, list; + DBusError err; + + dbus_error_init(&err); + + if (dbus_set_error_from_message(&err, reply) == TRUE) { + g_printerr("%s: %s\n", err.name, err.message); + dbus_error_free(&err); + goto done; + } + + if (dbus_message_has_signature(reply, "a(oa{sv})") == FALSE) + goto done; + + if (dbus_message_iter_init(reply, &iter) == FALSE) + goto done; + + dbus_message_iter_recurse(&iter, &list); + + while (dbus_message_iter_get_arg_type(&list) == DBUS_TYPE_STRUCT) { + DBusMessageIter entry, dict; + const char *path; + + dbus_message_iter_recurse(&list, &entry); + dbus_message_iter_get_basic(&entry, &path); + + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &dict); + + create_modem(conn, path, &entry); + + dbus_message_iter_next(&list); + } + +done: + dbus_message_unref(reply); +} + +static int get_modems(DBusConnection *conn) +{ + DBusMessage *msg; + DBusPendingCall *call; + + msg = dbus_message_new_method_call(OFONO_SERVICE, "/", + OFONO_MANAGER_INTERFACE, "GetModems"); + if (msg == NULL) + return -ENOMEM; + + dbus_message_set_auto_start(msg, FALSE); + + g_print("getting modems\n"); + + if (dbus_connection_send_with_reply(conn, msg, &call, -1) == FALSE) { + dbus_message_unref(msg); + return -EIO; + } + + dbus_message_unref(msg); + + if (call == NULL) + return -EINVAL; + + dbus_pending_call_set_notify(call, get_modems_reply, conn, NULL); + + dbus_pending_call_unref(call); + + return 0; +} + +static gboolean ofono_running = FALSE; + +static guint modem_added_watch; +static guint modem_removed_watch; +static guint modem_changed_watch; + +static void ofono_connect(DBusConnection *conn, void *user_data) +{ + g_print("starting telephony interface\n"); + + ofono_running = TRUE; + + modem_list = g_hash_table_new_full(g_str_hash, g_str_equal, + NULL, destroy_modem); + + modem_added_watch = g_dbus_add_signal_watch(conn, NULL, NULL, + OFONO_MANAGER_INTERFACE, "ModemAdded", + modem_added, NULL, NULL); + modem_removed_watch = g_dbus_add_signal_watch(conn, NULL, NULL, + OFONO_MANAGER_INTERFACE, "ModemRemoved", + modem_removed, NULL, NULL); + modem_changed_watch = g_dbus_add_signal_watch(conn, NULL, NULL, + OFONO_MODEM_INTERFACE, "PropertyChanged", + modem_changed, NULL, NULL); + + get_modems(conn); +} + +static void ofono_disconnect(DBusConnection *conn, void *user_data) +{ + g_print("stopping telephony interface\n"); + + ofono_running = FALSE; + + g_dbus_remove_watch(conn, modem_added_watch); + modem_added_watch = 0; + g_dbus_remove_watch(conn, modem_removed_watch); + modem_removed_watch = 0; + g_dbus_remove_watch(conn, modem_changed_watch); + modem_changed_watch = 0; + + g_hash_table_destroy(modem_list); + modem_list = NULL; +} + +static GMainLoop *main_loop = NULL; + +static volatile sig_atomic_t __terminated = 0; + +static void sig_term(int sig) +{ + if (__terminated > 0) + return; + + __terminated = 1; + + g_print("Terminating\n"); + + g_main_loop_quit(main_loop); +} + +static void disconnect_callback(DBusConnection *conn, void *user_data) +{ + g_printerr("D-Bus disconnect\n"); + + g_main_loop_quit(main_loop); +} + +static gboolean option_version = FALSE; + +static GOptionEntry options[] = { + { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version, + "Show version information and exit" }, + { NULL }, +}; + +int main(int argc, char **argv) +{ + GOptionContext *context; + GError *error = NULL; + DBusConnection *conn; + DBusError err; + guint watch; + struct sigaction sa; + +#ifdef NEED_THREADS + if (g_thread_supported() == FALSE) + g_thread_init(NULL); +#endif + + context = g_option_context_new(NULL); + g_option_context_add_main_entries(context, options, NULL); + + if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) { + if (error != NULL) { + g_printerr("%s\n", error->message); + g_error_free(error); + } else + g_printerr("An unknown error occurred\n"); + exit(1); + } + + g_option_context_free(context); + + if (option_version == TRUE) { + printf("%s\n", VERSION); + exit(0); + } + + main_loop = g_main_loop_new(NULL, FALSE); + +#ifdef NEED_THREADS + if (dbus_threads_init_default() == FALSE) { + fprintf(stderr, "Can't init usage of threads\n"); + exit(1); + } +#endif + + dbus_error_init(&err); + + conn = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, &err); + if (conn == NULL) { + if (dbus_error_is_set(&err) == TRUE) { + fprintf(stderr, "%s\n", err.message); + dbus_error_free(&err); + } else + fprintf(stderr, "Can't register with system bus\n"); + exit(1); + } + + g_dbus_set_disconnect_function(conn, disconnect_callback, NULL, NULL); + + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sig_term; + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + watch = g_dbus_add_service_watch(conn, OFONO_SERVICE, + ofono_connect, ofono_disconnect, NULL, NULL); + + g_main_loop_run(main_loop); + + g_dbus_remove_watch(conn, watch); + + if (ofono_running == TRUE) + ofono_disconnect(conn, NULL); + + dbus_connection_unref(conn); + + g_main_loop_unref(main_loop); + + return 0; +}