Convert Bluetooth plugin into a device driver
[connman] / plugins / bluetooth.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2008  Intel Corporation. All rights reserved.
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License version 2 as
9  *  published by the Free Software Foundation.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <errno.h>
27
28 #include <gdbus.h>
29
30 #include <connman/plugin.h>
31 #include <connman/device.h>
32 #include <connman/driver.h>
33 #include <connman/dbus.h>
34 #include <connman/log.h>
35
36 #define BLUEZ_SERVICE                   "org.bluez"
37 #define BLUEZ_MANAGER_INTERFACE         BLUEZ_SERVICE ".Manager"
38 #define BLUEZ_ADAPTER_INTERFACE         BLUEZ_SERVICE ".Adapter"
39
40 #define LIST_ADAPTERS                   "ListAdapters"
41 #define ADAPTER_ADDED                   "AdapterAdded"
42 #define ADAPTER_REMOVED                 "AdapterRemoved"
43
44 #define PROPERTY_CHANGED                "PropertyChanged"
45 #define GET_PROPERTIES                  "GetProperties"
46 #define SET_PROPERTY                    "SetProperty"
47
48 #define TIMEOUT 5000
49
50 struct adapter_data {
51         DBusConnection *connection;
52 };
53
54 static int bluetooth_probe(struct connman_device *adapter)
55 {
56         struct adapter_data *data;
57
58         DBG("adapter %p", adapter);
59
60         data = g_try_new0(struct adapter_data, 1);
61         if (data == NULL)
62                 return -ENOMEM;
63
64         data->connection = connman_dbus_get_connection();
65         if (data->connection == NULL) {
66                 g_free(data);
67                 return -EIO;
68         }
69
70         connman_device_set_data(adapter, data);
71
72         return 0;
73 }
74
75 static void bluetooth_remove(struct connman_device *adapter)
76 {
77         struct adapter_data *data = connman_device_get_data(adapter);
78
79         DBG("adapter %p", adapter);
80
81         connman_device_set_data(adapter, NULL);
82
83         dbus_connection_unref(data->connection);
84
85         g_free(data);
86 }
87
88 static void powered_reply(DBusPendingCall *call, void *user_data)
89 {
90         DBusMessage *reply;
91
92         DBG("");
93
94         reply = dbus_pending_call_steal_reply(call);
95
96         dbus_message_unref(reply);
97 }
98
99 static int change_powered(DBusConnection *connection, const char *path,
100                                                         dbus_bool_t powered)
101 {
102         DBusMessage *message;
103         DBusMessageIter iter;
104         DBusPendingCall *call;
105
106         DBG("");
107
108         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
109                                         BLUEZ_ADAPTER_INTERFACE, SET_PROPERTY);
110         if (message == NULL)
111                 return -ENOMEM;
112
113         dbus_message_iter_init_append(message, &iter);
114         connman_dbus_property_append_variant(&iter, "Powered",
115                                                 DBUS_TYPE_BOOLEAN, &powered);
116
117         if (dbus_connection_send_with_reply(connection, message,
118                                                 &call, TIMEOUT) == FALSE) {
119                 connman_error("Failed to change Powered property");
120                 dbus_message_unref(message);
121                 return -EINVAL;
122         }
123
124         dbus_pending_call_set_notify(call, powered_reply, NULL, NULL);
125
126         dbus_message_unref(message);
127
128         return -EINPROGRESS;
129 }
130
131 static int bluetooth_enable(struct connman_device *adapter)
132 {
133         struct adapter_data *data = connman_device_get_data(adapter);
134
135         DBG("adapter %p", adapter);
136
137         return change_powered(data->connection,
138                                         adapter->element->devpath, TRUE);
139 }
140
141 static int bluetooth_disable(struct connman_device *adapter)
142 {
143         struct adapter_data *data = connman_device_get_data(adapter);
144
145         DBG("adapter %p", adapter);
146
147         return change_powered(data->connection,
148                                         adapter->element->devpath, FALSE);
149 }
150
151 static int bluetooth_scan(struct connman_device *adapter)
152 {
153         DBG("adapter %p", adapter);
154
155         return -EIO;
156 }
157
158 static struct connman_device_driver bluetooth_driver = {
159         .name           = "bluetooth",
160         .type           = CONNMAN_DEVICE_TYPE_BLUETOOTH,
161         .probe          = bluetooth_probe,
162         .remove         = bluetooth_remove,
163         .enable         = bluetooth_enable,
164         .disable        = bluetooth_disable,
165         .scan           = bluetooth_scan,
166 };
167
168 static GSList *device_list = NULL;
169
170 static struct connman_element *find_adapter(const char *path)
171 {
172         GSList *list;
173
174         DBG("path %s", path);
175
176         for (list = device_list; list; list = list->next) {
177                 struct connman_element *device = list->data;
178
179                 if (g_str_equal(device->devpath, path) == TRUE)
180                         return device;
181         }
182
183         return NULL;
184 }
185
186 static void check_devices(struct connman_element *adapter,
187                                                 DBusMessageIter *array)
188 {
189         DBusMessageIter value;
190
191         DBG("adapter %p name %s", adapter, adapter->name);
192
193         if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
194                 return;
195
196         dbus_message_iter_recurse(array, &value);
197
198         while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_OBJECT_PATH) {
199                 const char *path;
200
201                 dbus_message_iter_get_basic(&value, &path);
202
203                 DBG("device %s", path);
204
205                 dbus_message_iter_next(&value);
206         }
207 }
208
209 static void property_changed(DBusConnection *connection, DBusMessage *message)
210 {
211         const char *path = dbus_message_get_path(message);
212         struct connman_element *device;
213         DBusMessageIter iter, value;
214         const char *key;
215
216         DBG("path %s", path);
217
218         device = find_adapter(path);
219         if (device == NULL)
220                 return;
221
222         if (dbus_message_iter_init(message, &iter) == FALSE)
223                 return;
224
225         dbus_message_iter_get_basic(&iter, &key);
226
227         dbus_message_iter_next(&iter);
228         dbus_message_iter_recurse(&iter, &value);
229
230         if (g_str_equal(key, "Powered") == TRUE) {
231                 struct connman_device *dev = connman_element_get_data(device);
232                 gboolean val;
233
234                 dbus_message_iter_get_basic(&value, &val);
235                 if (dev != NULL)
236                         connman_device_set_powered(dev, val);
237         } else if (g_str_equal(key, "Discovering") == TRUE) {
238                 struct connman_device *dev = connman_element_get_data(device);
239                 gboolean val;
240
241                 dbus_message_iter_get_basic(&value, &val);
242                 if (dev != NULL)
243                         connman_device_set_scanning(dev, val);
244         }
245 }
246
247 static void properties_reply(DBusPendingCall *call, void *user_data)
248 {
249         DBusMessage *message = user_data;
250         const char *path = dbus_message_get_path(message);
251         struct connman_element *device;
252         DBusMessageIter array, dict;
253         DBusMessage *reply;
254
255         DBG("path %s", path);
256
257         device = find_adapter(path);
258
259         dbus_message_unref(message);
260
261         reply = dbus_pending_call_steal_reply(call);
262
263         if (device == NULL)
264                 goto done;
265
266         if (dbus_message_iter_init(reply, &array) == FALSE)
267                 goto done;
268
269         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
270                 goto done;
271
272         dbus_message_iter_recurse(&array, &dict);
273         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
274                 struct connman_device *dev = connman_element_get_data(device);
275                 DBusMessageIter entry, value;
276                 const char *key;
277
278                 dbus_message_iter_recurse(&dict, &entry);
279                 dbus_message_iter_get_basic(&entry, &key);
280
281                 dbus_message_iter_next(&entry);
282                 dbus_message_iter_recurse(&entry, &value);
283
284                 if (g_str_equal(key, "Powered") == TRUE) {
285                         gboolean val;
286
287                         dbus_message_iter_get_basic(&value, &val);
288                         if (dev != NULL)
289                                 connman_device_set_powered(dev, val);
290                 } else if (g_str_equal(key, "Discovering") == TRUE) {
291                         gboolean val;
292
293                         dbus_message_iter_get_basic(&value, &val);
294                         if (dev != NULL)
295                                 connman_device_set_scanning(dev, val);
296                 } else if (g_str_equal(key, "Devices") == TRUE) {
297                         check_devices(device, &value);
298                 }
299
300                 dbus_message_iter_next(&dict);
301         }
302
303 done:
304         dbus_message_unref(reply);
305 }
306
307 static void add_adapter(DBusConnection *connection, const char *path)
308 {
309         struct connman_element *device;
310         DBusMessage *message;
311         DBusPendingCall *call;
312
313         DBG("path %s", path);
314
315         device = find_adapter(path);
316         if (device != NULL)
317                 return;
318
319         device = connman_element_create(NULL);
320         device->type = CONNMAN_ELEMENT_TYPE_DEVICE;
321         device->subtype = CONNMAN_ELEMENT_SUBTYPE_BLUETOOTH;
322         device->policy = CONNMAN_ELEMENT_POLICY_IGNORE;
323
324         device->name = g_path_get_basename(path);
325         device->devpath = g_strdup(path);
326
327         if (connman_element_register(device, NULL) < 0) {
328                 connman_element_unref(device);
329                 return;
330         }
331
332         device_list = g_slist_append(device_list, device);
333
334         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
335                                 BLUEZ_ADAPTER_INTERFACE, GET_PROPERTIES);
336         if (message == NULL)
337                 return;
338
339         if (dbus_connection_send_with_reply(connection, message,
340                                                 &call, TIMEOUT) == FALSE) {
341                 connman_error("Failed to get adapter properties");
342                 dbus_message_unref(message);
343                 return;
344         }
345
346         dbus_pending_call_set_notify(call, properties_reply, message, NULL);
347 }
348
349 static void remove_adapter(DBusConnection *connection, const char *path)
350 {
351         struct connman_element *device;
352
353         DBG("path %s", path);
354
355         device = find_adapter(path);
356         if (device == NULL)
357                 return;
358
359         device_list = g_slist_remove(device_list, device);
360
361         connman_element_unregister(device);
362         connman_element_unref(device);
363 }
364
365 static void adapters_reply(DBusPendingCall *call, void *user_data)
366 {
367         DBusConnection *connection = user_data;
368         DBusMessage *reply;
369         DBusError error;
370         char **adapters;
371         int i, num_adapters;
372
373         DBG("");
374
375         reply = dbus_pending_call_steal_reply(call);
376
377         dbus_error_init(&error);
378
379         if (dbus_message_get_args(reply, &error,
380                                 DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
381                                                 &adapters, &num_adapters,
382                                                 DBUS_TYPE_INVALID) == FALSE) {
383                 if (dbus_error_is_set(&error) == TRUE) {
384                         connman_error("%s", error.message);
385                         dbus_error_free(&error);
386                 } else
387                         connman_error("Wrong arguments for adapter list");
388                 goto done;
389         }
390
391         for (i = 0; i < num_adapters; i++)
392                 add_adapter(connection, adapters[i]);
393
394         g_strfreev(adapters);
395
396 done:
397         dbus_message_unref(reply);
398 }
399
400 static void bluetooth_connect(DBusConnection *connection, void *user_data)
401 {
402         DBusMessage *message;
403         DBusPendingCall *call;
404
405         DBG("connection %p", connection);
406
407         message = dbus_message_new_method_call(BLUEZ_SERVICE, "/",
408                                 BLUEZ_MANAGER_INTERFACE, LIST_ADAPTERS);
409         if (message == NULL)
410                 return;
411
412         if (dbus_connection_send_with_reply(connection, message,
413                                                 &call, TIMEOUT) == FALSE) {
414                 connman_error("Failed to get Bluetooth adapters");
415                 dbus_message_unref(message);
416                 return;
417         }
418
419         dbus_pending_call_set_notify(call, adapters_reply, connection, NULL);
420
421         dbus_message_unref(message);
422 }
423
424 static void bluetooth_disconnect(DBusConnection *connection, void *user_data)
425 {
426         GSList *list;
427
428         DBG("connection %p", connection);
429
430         for (list = device_list; list; list = list->next) {
431                 struct connman_element *device = list->data;
432
433                 connman_element_unregister(device);
434                 connman_element_unref(device);
435         }
436
437         g_slist_free(device_list);
438         device_list = NULL;
439 }
440
441 static DBusHandlerResult bluetooth_signal(DBusConnection *connection,
442                                         DBusMessage *message, void *user_data)
443 {
444         DBG("connection %p", connection);
445
446         if (dbus_message_is_signal(message, BLUEZ_ADAPTER_INTERFACE,
447                                                 PROPERTY_CHANGED) == TRUE) {
448                 property_changed(connection, message);
449         } else if (dbus_message_is_signal(message, BLUEZ_MANAGER_INTERFACE,
450                                                 ADAPTER_ADDED) == TRUE) {
451                 const char *path;
452                 dbus_message_get_args(message, NULL,
453                                         DBUS_TYPE_OBJECT_PATH, &path,
454                                                         DBUS_TYPE_INVALID);
455                 add_adapter(connection, path);
456         } else if (dbus_message_is_signal(message, BLUEZ_MANAGER_INTERFACE,
457                                                 ADAPTER_REMOVED) == TRUE) {
458                 const char *path;
459                 dbus_message_get_args(message, NULL,
460                                         DBUS_TYPE_OBJECT_PATH, &path,
461                                                         DBUS_TYPE_INVALID);
462                 remove_adapter(connection, path);
463         }
464
465         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
466 }
467
468 static DBusConnection *connection;
469 static guint watch;
470
471 static const char *added_rule = "type=signal,member=" ADAPTER_ADDED
472                                         ",interface=" BLUEZ_MANAGER_INTERFACE;
473 static const char *removed_rule = "type=signal,member=" ADAPTER_REMOVED
474                                         ",interface=" BLUEZ_MANAGER_INTERFACE;
475
476 static const char *adapter_rule = "type=signal,member=" PROPERTY_CHANGED
477                                         ",interface=" BLUEZ_ADAPTER_INTERFACE;
478
479 static int bluetooth_init(void)
480 {
481         int err = -EIO;
482
483         connection = connman_dbus_get_connection();
484         if (connection == NULL)
485                 return -EIO;
486
487         if (dbus_connection_add_filter(connection, bluetooth_signal,
488                                                         NULL, NULL) == FALSE)
489                 goto unref;
490
491         err = connman_device_driver_register(&bluetooth_driver);
492         if (err < 0)
493                 goto remove;
494
495         watch = g_dbus_add_service_watch(connection, BLUEZ_SERVICE,
496                         bluetooth_connect, bluetooth_disconnect, NULL, NULL);
497         if (watch == 0) {
498                 connman_device_driver_unregister(&bluetooth_driver);
499                 err = -EIO;
500                 goto remove;
501         }
502
503         if (g_dbus_check_service(connection, BLUEZ_SERVICE) == TRUE)
504                 bluetooth_connect(connection, NULL);
505
506         dbus_bus_add_match(connection, added_rule, NULL);
507         dbus_bus_add_match(connection, removed_rule, NULL);
508         dbus_bus_add_match(connection, adapter_rule, NULL);
509         dbus_connection_flush(connection);
510
511         return 0;
512
513 remove:
514         dbus_connection_remove_filter(connection, bluetooth_signal, NULL);
515
516 unref:
517         dbus_connection_unref(connection);
518
519         return err;
520 }
521
522 static void bluetooth_exit(void)
523 {
524         dbus_bus_remove_match(connection, adapter_rule, NULL);
525         dbus_bus_remove_match(connection, removed_rule, NULL);
526         dbus_bus_remove_match(connection, added_rule, NULL);
527         dbus_connection_flush(connection);
528
529         g_dbus_remove_watch(connection, watch);
530
531         bluetooth_disconnect(connection, NULL);
532
533         connman_device_driver_unregister(&bluetooth_driver);
534
535         dbus_connection_remove_filter(connection, bluetooth_signal, NULL);
536
537         dbus_connection_unref(connection);
538 }
539
540 CONNMAN_PLUGIN_DEFINE(bluetooth, "Bluetooth technology plugin", VERSION,
541                                                 bluetooth_init, bluetooth_exit)