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