Home | History | Annotate | Download | only in plugins
      1 /*
      2  *
      3  *  BlueZ - Bluetooth protocol stack for Linux
      4  *
      5  *  Copyright (C) 2006-2010  Nokia Corporation
      6  *  Copyright (C) 2004-2010  Marcel Holtmann <marcel (at) holtmann.org>
      7  *
      8  *
      9  *  This program is free software; you can redistribute it and/or modify
     10  *  it under the terms of the GNU General Public License as published by
     11  *  the Free Software Foundation; either version 2 of the License, or
     12  *  (at your option) any later version.
     13  *
     14  *  This program is distributed in the hope that it will be useful,
     15  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
     16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17  *  GNU General Public License for more details.
     18  *
     19  *  You should have received a copy of the GNU General Public License
     20  *  along with this program; if not, write to the Free Software
     21  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
     22  *
     23  */
     24 
     25 #ifdef HAVE_CONFIG_H
     26 #include <config.h>
     27 #endif
     28 
     29 #include <glib.h>
     30 #include <dbus/dbus.h>
     31 
     32 #include "adapter.h"
     33 #include "plugin.h"
     34 #include "log.h"
     35 #include "gdbus.h"
     36 
     37 /* from mce/mode-names.h */
     38 #define MCE_RADIO_STATE_BLUETOOTH	(1 << 3)
     39 
     40 /* from mce/dbus-names.h */
     41 #define MCE_SERVICE			"com.nokia.mce"
     42 #define MCE_REQUEST_IF			"com.nokia.mce.request"
     43 #define MCE_SIGNAL_IF			"com.nokia.mce.signal"
     44 #define MCE_REQUEST_PATH		"/com/nokia/mce/request"
     45 #define MCE_SIGNAL_PATH			"/com/nokia/mce/signal"
     46 #define MCE_RADIO_STATES_CHANGE_REQ	"req_radio_states_change"
     47 #define MCE_RADIO_STATES_GET		"get_radio_states"
     48 #define MCE_RADIO_STATES_SIG		"radio_states_ind"
     49 
     50 static guint watch_id;
     51 static DBusConnection *conn = NULL;
     52 static gboolean mce_bt_set = FALSE;
     53 static gboolean collision = FALSE;
     54 
     55 static gboolean mce_signal_callback(DBusConnection *connection,
     56 					DBusMessage *message, void *user_data)
     57 {
     58 	DBusMessageIter args;
     59 	uint32_t sigvalue;
     60 	struct btd_adapter *adapter = user_data;
     61 
     62 	DBG("received mce signal");
     63 
     64 	if (!dbus_message_iter_init(message, &args))
     65 		error("message has no arguments");
     66 	else if (DBUS_TYPE_UINT32 != dbus_message_iter_get_arg_type(&args))
     67 		error("argument is not uint32");
     68 	else {
     69 		dbus_message_iter_get_basic(&args, &sigvalue);
     70 		DBG("got signal with value %u", sigvalue);
     71 
     72 		/* set the adapter according to the mce signal
     73 		   and remember the value */
     74 		mce_bt_set = sigvalue & MCE_RADIO_STATE_BLUETOOTH ?
     75 								TRUE : FALSE;
     76 
     77 		if (mce_bt_set)
     78 			btd_adapter_switch_online(adapter);
     79 		else
     80 			btd_adapter_switch_offline(adapter);
     81 	}
     82 
     83 	return TRUE;
     84 }
     85 
     86 static void read_radio_states_cb(DBusPendingCall *call, void *user_data)
     87 {
     88 	DBusError err;
     89 	DBusMessage *reply;
     90 	dbus_uint32_t radio_states;
     91 	struct btd_adapter *adapter = user_data;
     92 
     93 	reply = dbus_pending_call_steal_reply(call);
     94 
     95 	dbus_error_init(&err);
     96 	if (dbus_set_error_from_message(&err, reply)) {
     97 		error("mce replied with an error: %s, %s",
     98 				err.name, err.message);
     99 		dbus_error_free(&err);
    100 		goto done;
    101 	}
    102 
    103 	dbus_error_init(&err);
    104 	if (dbus_message_get_args(reply, &err,
    105 				DBUS_TYPE_UINT32, &radio_states,
    106 				DBUS_TYPE_INVALID) == FALSE) {
    107 		error("unable to parse get_radio_states reply: %s, %s",
    108 							err.name, err.message);
    109 		dbus_error_free(&err);
    110 		goto done;
    111 	}
    112 
    113 	DBG("radio_states: %d", radio_states);
    114 
    115 	mce_bt_set = radio_states & MCE_RADIO_STATE_BLUETOOTH ? TRUE : FALSE;
    116 
    117 	/* check if the adapter has not completed the initial power
    118 	 * cycle, if so delay action to mce_notify_powered */
    119 	collision = mce_bt_set && adapter_powering_down(adapter);
    120 
    121 	if (collision)
    122 		goto done;
    123 
    124 	if (mce_bt_set)
    125 		btd_adapter_switch_online(adapter);
    126 	else
    127 		btd_adapter_switch_offline(adapter);
    128 
    129 done:
    130 	dbus_message_unref(reply);
    131 }
    132 
    133 static void adapter_powered(struct btd_adapter *adapter, gboolean powered)
    134 {
    135 	DBusMessage *msg;
    136 	dbus_uint32_t radio_states = 0;
    137 	dbus_uint32_t radio_mask = MCE_RADIO_STATE_BLUETOOTH;
    138 	static gboolean startup = TRUE;
    139 
    140 	DBG("adapter_powered called with %d", powered);
    141 
    142 	if (startup) {
    143 		startup = FALSE;
    144 		return;
    145 	}
    146 
    147 	/* check if the plugin got the get_radio_states reply from the
    148 	 * mce when the adapter was not yet down during the power
    149 	 * cycling when bluetoothd is started */
    150 	if (collision) {
    151 		error("maemo6: powered state collision");
    152 		collision = FALSE;
    153 
    154 		if (mce_bt_set)
    155 			btd_adapter_switch_online(adapter);
    156 
    157 		return;
    158 	}
    159 
    160 	/* nothing to do if the states match */
    161 	if (mce_bt_set == powered)
    162 		return;
    163 
    164 	/* set the mce value according to the state of the adapter */
    165 	msg = dbus_message_new_method_call(MCE_SERVICE, MCE_REQUEST_PATH,
    166 				MCE_REQUEST_IF, MCE_RADIO_STATES_CHANGE_REQ);
    167 
    168 	if (powered)
    169 		radio_states = MCE_RADIO_STATE_BLUETOOTH;
    170 
    171 	dbus_message_append_args(msg, DBUS_TYPE_UINT32, &radio_states,
    172 				DBUS_TYPE_UINT32, &radio_mask,
    173 				DBUS_TYPE_INVALID);
    174 
    175 	if (dbus_connection_send(conn, msg, NULL))
    176 		mce_bt_set = powered;
    177 	else
    178 		error("calling %s failed", MCE_RADIO_STATES_CHANGE_REQ);
    179 
    180 	dbus_message_unref(msg);
    181 }
    182 
    183 static int mce_probe(struct btd_adapter *adapter)
    184 {
    185 	DBusMessage *msg;
    186 	DBusPendingCall *call;
    187 
    188 	DBG("path %s", adapter_get_path(adapter));
    189 
    190 	msg = dbus_message_new_method_call(MCE_SERVICE, MCE_REQUEST_PATH,
    191 					MCE_REQUEST_IF, MCE_RADIO_STATES_GET);
    192 
    193 	if (!dbus_connection_send_with_reply(conn, msg, &call, -1)) {
    194 		error("calling %s failed", MCE_RADIO_STATES_GET);
    195 		dbus_message_unref(msg);
    196 		return -1;
    197 	}
    198 
    199 	dbus_pending_call_set_notify(call, read_radio_states_cb, adapter, NULL);
    200 	dbus_pending_call_unref(call);
    201 	dbus_message_unref(msg);
    202 
    203 	watch_id = g_dbus_add_signal_watch(conn, NULL, MCE_SIGNAL_PATH,
    204 					MCE_SIGNAL_IF, MCE_RADIO_STATES_SIG,
    205 					mce_signal_callback, adapter, NULL);
    206 
    207 	btd_adapter_register_powered_callback(adapter, adapter_powered);
    208 
    209 	return 0;
    210 }
    211 
    212 static void mce_remove(struct btd_adapter *adapter)
    213 {
    214 	DBG("path %s", adapter_get_path(adapter));
    215 
    216 	if (watch_id > 0)
    217 		g_dbus_remove_watch(conn, watch_id);
    218 
    219 	btd_adapter_unregister_powered_callback(adapter, adapter_powered);
    220 }
    221 
    222 static struct btd_adapter_driver mce_driver = {
    223 	.name	= "mce",
    224 	.probe	= mce_probe,
    225 	.remove	= mce_remove,
    226 };
    227 
    228 static int maemo6_init(void)
    229 {
    230 	DBG("init maemo6 plugin");
    231 
    232 	conn = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
    233 	if (conn == NULL) {
    234 		error("Unable to connect to D-Bus");
    235 		return -1;
    236 	}
    237 
    238 	return btd_register_adapter_driver(&mce_driver);
    239 }
    240 
    241 static void maemo6_exit(void)
    242 {
    243 	DBG("exit maemo6 plugin");
    244 
    245 	if (conn != NULL)
    246 		dbus_connection_unref(conn);
    247 
    248 	btd_unregister_adapter_driver(&mce_driver);
    249 }
    250 
    251 BLUETOOTH_PLUGIN_DEFINE(maemo6, VERSION,
    252 		BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, maemo6_init, maemo6_exit)
    253