/* * * BlueZ - Bluetooth protocol stack for Linux * * Copyright (C) 2006-2010 Nokia Corporation * Copyright (C) 2004-2010 Marcel Holtmann * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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 "adapter.h" #include "plugin.h" #include "log.h" #include "gdbus.h" /* from mce/mode-names.h */ #define MCE_RADIO_STATE_BLUETOOTH (1 << 3) /* from mce/dbus-names.h */ #define MCE_SERVICE "com.nokia.mce" #define MCE_REQUEST_IF "com.nokia.mce.request" #define MCE_SIGNAL_IF "com.nokia.mce.signal" #define MCE_REQUEST_PATH "/com/nokia/mce/request" #define MCE_SIGNAL_PATH "/com/nokia/mce/signal" #define MCE_RADIO_STATES_CHANGE_REQ "req_radio_states_change" #define MCE_RADIO_STATES_GET "get_radio_states" #define MCE_RADIO_STATES_SIG "radio_states_ind" static guint watch_id; static DBusConnection *conn = NULL; static gboolean mce_bt_set = FALSE; static gboolean collision = FALSE; static gboolean mce_signal_callback(DBusConnection *connection, DBusMessage *message, void *user_data) { DBusMessageIter args; uint32_t sigvalue; struct btd_adapter *adapter = user_data; DBG("received mce signal"); if (!dbus_message_iter_init(message, &args)) error("message has no arguments"); else if (DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type(&args)) error("argument is not uint32"); else { dbus_message_iter_get_basic(&args, &sigvalue); DBG("got signal with value %u", sigvalue); /* set the adapter according to the mce signal and remember the value */ mce_bt_set = sigvalue & MCE_RADIO_STATE_BLUETOOTH ? TRUE : FALSE; if (mce_bt_set) btd_adapter_switch_online(adapter); else btd_adapter_switch_offline(adapter); } return TRUE; } static void read_radio_states_cb(DBusPendingCall *call, void *user_data) { DBusError err; DBusMessage *reply; dbus_uint32_t radio_states; struct btd_adapter *adapter = user_data; reply = dbus_pending_call_steal_reply(call); dbus_error_init(&err); if (dbus_set_error_from_message(&err, reply)) { error("mce replied with an error: %s, %s", err.name, err.message); dbus_error_free(&err); goto done; } dbus_error_init(&err); if (dbus_message_get_args(reply, &err, DBUS_TYPE_UINT32, &radio_states, DBUS_TYPE_INVALID) == FALSE) { error("unable to parse get_radio_states reply: %s, %s", err.name, err.message); dbus_error_free(&err); goto done; } DBG("radio_states: %d", radio_states); mce_bt_set = radio_states & MCE_RADIO_STATE_BLUETOOTH ? TRUE : FALSE; /* check if the adapter has not completed the initial power * cycle, if so delay action to mce_notify_powered */ collision = mce_bt_set && adapter_powering_down(adapter); if (collision) goto done; if (mce_bt_set) btd_adapter_switch_online(adapter); else btd_adapter_switch_offline(adapter); done: dbus_message_unref(reply); } static void adapter_powered(struct btd_adapter *adapter, gboolean powered) { DBusMessage *msg; dbus_uint32_t radio_states = 0; dbus_uint32_t radio_mask = MCE_RADIO_STATE_BLUETOOTH; static gboolean startup = TRUE; DBG("adapter_powered called with %d", powered); if (startup) { startup = FALSE; return; } /* check if the plugin got the get_radio_states reply from the * mce when the adapter was not yet down during the power * cycling when bluetoothd is started */ if (collision) { error("maemo6: powered state collision"); collision = FALSE; if (mce_bt_set) btd_adapter_switch_online(adapter); return; } /* nothing to do if the states match */ if (mce_bt_set == powered) return; /* set the mce value according to the state of the adapter */ msg = dbus_message_new_method_call(MCE_SERVICE, MCE_REQUEST_PATH, MCE_REQUEST_IF, MCE_RADIO_STATES_CHANGE_REQ); if (powered) radio_states = MCE_RADIO_STATE_BLUETOOTH; dbus_message_append_args(msg, DBUS_TYPE_UINT32, &radio_states, DBUS_TYPE_UINT32, &radio_mask, DBUS_TYPE_INVALID); if (dbus_connection_send(conn, msg, NULL)) mce_bt_set = powered; else error("calling %s failed", MCE_RADIO_STATES_CHANGE_REQ); dbus_message_unref(msg); } static int mce_probe(struct btd_adapter *adapter) { DBusMessage *msg; DBusPendingCall *call; DBG("path %s", adapter_get_path(adapter)); msg = dbus_message_new_method_call(MCE_SERVICE, MCE_REQUEST_PATH, MCE_REQUEST_IF, MCE_RADIO_STATES_GET); if (!dbus_connection_send_with_reply(conn, msg, &call, -1)) { error("calling %s failed", MCE_RADIO_STATES_GET); dbus_message_unref(msg); return -1; } dbus_pending_call_set_notify(call, read_radio_states_cb, adapter, NULL); dbus_pending_call_unref(call); dbus_message_unref(msg); watch_id = g_dbus_add_signal_watch(conn, NULL, MCE_SIGNAL_PATH, MCE_SIGNAL_IF, MCE_RADIO_STATES_SIG, mce_signal_callback, adapter, NULL); btd_adapter_register_powered_callback(adapter, adapter_powered); return 0; } static void mce_remove(struct btd_adapter *adapter) { DBG("path %s", adapter_get_path(adapter)); if (watch_id > 0) g_dbus_remove_watch(conn, watch_id); btd_adapter_unregister_powered_callback(adapter, adapter_powered); } static struct btd_adapter_driver mce_driver = { .name = "mce", .probe = mce_probe, .remove = mce_remove, }; static int maemo6_init(void) { DBG("init maemo6 plugin"); conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL); if (conn == NULL) { error("Unable to connect to D-Bus"); return -1; } return btd_register_adapter_driver(&mce_driver); } static void maemo6_exit(void) { DBG("exit maemo6 plugin"); if (conn != NULL) dbus_connection_unref(conn); btd_unregister_adapter_driver(&mce_driver); } BLUETOOTH_PLUGIN_DEFINE(maemo6, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, maemo6_init, maemo6_exit)