• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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 
mce_signal_callback(DBusConnection * connection,DBusMessage * message,void * user_data)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 
read_radio_states_cb(DBusPendingCall * call,void * user_data)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 
adapter_powered(struct btd_adapter * adapter,gboolean powered)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 
mce_probe(struct btd_adapter * adapter)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 
mce_remove(struct btd_adapter * adapter)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 
maemo6_init(void)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 
maemo6_exit(void)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