Add initial support for Bluetooth networks
[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 #include <stdlib.h>
28
29 #include <gdbus.h>
30
31 #include <connman/plugin.h>
32 #include <connman/device.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 #define BLUEZ_DEVICE_INTERFACE          BLUEZ_SERVICE ".Device"
40
41 #define LIST_ADAPTERS                   "ListAdapters"
42 #define ADAPTER_ADDED                   "AdapterAdded"
43 #define ADAPTER_REMOVED                 "AdapterRemoved"
44
45 #define PROPERTY_CHANGED                "PropertyChanged"
46 #define GET_PROPERTIES                  "GetProperties"
47 #define SET_PROPERTY                    "SetProperty"
48
49 #define TIMEOUT 5000
50
51 typedef void (* properties_callback_t) (DBusConnection *connection,
52                                                         const char *path,
53                                                         DBusMessage *message,
54                                                         void *user_data);
55
56 struct properties_data {
57         DBusConnection *connection;
58         DBusMessage *message;
59         properties_callback_t callback;
60         void *user_data;
61 };
62
63 static void get_properties_reply(DBusPendingCall *call, void *user_data)
64 {
65         struct properties_data *data = user_data;
66         DBusMessage *reply;
67         const char *path;
68
69         reply = dbus_pending_call_steal_reply(call);
70         if (reply == NULL)
71                 goto done;
72
73         path = dbus_message_get_path(data->message);
74
75         data->callback(data->connection, path, reply, data->user_data);
76
77         dbus_message_unref(reply);
78
79 done:
80         dbus_message_unref(data->message);
81         g_free(data);
82 }
83
84 static void get_properties(DBusConnection *connection,
85                                 const char *path, const char *interface,
86                                 properties_callback_t callback, void *user_data)
87 {
88         struct properties_data *data;
89         DBusMessage *message;
90         DBusPendingCall *call;
91
92         DBG("path %s interface %s", path, interface);
93
94         data = g_try_new0(struct properties_data, 1);
95         if (data == NULL)
96                 return;
97
98         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
99                                                 interface, GET_PROPERTIES);
100         if (message == NULL) {
101                 g_free(data);
102                 return;
103         }
104
105         if (dbus_connection_send_with_reply(connection, message,
106                                                 &call, TIMEOUT) == FALSE) {
107                 connman_error("Failed to get properties for %s", interface);
108                 dbus_message_unref(message);
109                 g_free(data);
110                 return;
111         }
112
113         data->connection = connection;
114         data->message    = message;
115         data->callback   = callback;
116         data->user_data  = user_data;
117
118         dbus_pending_call_set_notify(call, get_properties_reply, data, NULL);
119 }
120
121 struct adapter_data {
122         DBusConnection *connection;
123 };
124
125 static int bluetooth_probe(struct connman_device *adapter)
126 {
127         struct adapter_data *data;
128
129         DBG("adapter %p", adapter);
130
131         data = g_try_new0(struct adapter_data, 1);
132         if (data == NULL)
133                 return -ENOMEM;
134
135         data->connection = connman_dbus_get_connection();
136         if (data->connection == NULL) {
137                 g_free(data);
138                 return -EIO;
139         }
140
141         connman_device_set_data(adapter, data);
142
143         return 0;
144 }
145
146 static void bluetooth_remove(struct connman_device *adapter)
147 {
148         struct adapter_data *data = connman_device_get_data(adapter);
149
150         DBG("adapter %p", adapter);
151
152         connman_device_set_data(adapter, NULL);
153
154         dbus_connection_unref(data->connection);
155
156         g_free(data);
157 }
158
159 static void powered_reply(DBusPendingCall *call, void *user_data)
160 {
161         DBusMessage *reply;
162
163         DBG("");
164
165         reply = dbus_pending_call_steal_reply(call);
166
167         dbus_message_unref(reply);
168 }
169
170 static int change_powered(DBusConnection *connection, const char *path,
171                                                         dbus_bool_t powered)
172 {
173         DBusMessage *message;
174         DBusMessageIter iter;
175         DBusPendingCall *call;
176
177         DBG("");
178
179         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
180                                         BLUEZ_ADAPTER_INTERFACE, SET_PROPERTY);
181         if (message == NULL)
182                 return -ENOMEM;
183
184         dbus_message_iter_init_append(message, &iter);
185         connman_dbus_property_append_variant(&iter, "Powered",
186                                                 DBUS_TYPE_BOOLEAN, &powered);
187
188         if (dbus_connection_send_with_reply(connection, message,
189                                                 &call, TIMEOUT) == FALSE) {
190                 connman_error("Failed to change Powered property");
191                 dbus_message_unref(message);
192                 return -EINVAL;
193         }
194
195         dbus_pending_call_set_notify(call, powered_reply, NULL, NULL);
196
197         dbus_message_unref(message);
198
199         return -EINPROGRESS;
200 }
201
202 static int bluetooth_enable(struct connman_device *adapter)
203 {
204         struct adapter_data *data = connman_device_get_data(adapter);
205         const char *path = connman_device_get_path(adapter);
206
207         DBG("adapter %p", adapter);
208
209         return change_powered(data->connection, path, TRUE);
210 }
211
212 static int bluetooth_disable(struct connman_device *adapter)
213 {
214         struct adapter_data *data = connman_device_get_data(adapter);
215         const char *path = connman_device_get_path(adapter);
216
217         DBG("adapter %p", adapter);
218
219         return change_powered(data->connection, path, FALSE);
220 }
221
222 static int bluetooth_scan(struct connman_device *adapter)
223 {
224         DBG("adapter %p", adapter);
225
226         return -EIO;
227 }
228
229 static struct connman_device_driver bluetooth_driver = {
230         .name           = "bluetooth",
231         .type           = CONNMAN_DEVICE_TYPE_BLUETOOTH,
232         .probe          = bluetooth_probe,
233         .remove         = bluetooth_remove,
234         .enable         = bluetooth_enable,
235         .disable        = bluetooth_disable,
236         .scan           = bluetooth_scan,
237 };
238
239 static GSList *adapter_list = NULL;
240
241 static void free_adapters(void)
242 {
243         GSList *list;
244
245         DBG("");
246
247         for (list = adapter_list; list; list = list->next) {
248                 struct connman_device *adapter = list->data;
249
250                 connman_device_unregister(adapter);
251                 connman_device_unref(adapter);
252         }
253
254         g_slist_free(adapter_list);
255         adapter_list = NULL;
256 }
257
258 static struct connman_device *find_adapter(const char *path)
259 {
260         GSList *list;
261
262         DBG("path %s", path);
263
264         for (list = adapter_list; list; list = list->next) {
265                 struct connman_device *adapter = list->data;
266                 const char *adapter_path = connman_device_get_path(adapter);
267
268                 if (adapter_path == NULL)
269                         continue;
270
271                 if (g_str_equal(adapter_path, path) == TRUE)
272                         return adapter;
273         }
274
275         return NULL;
276 }
277
278 static void device_properties(DBusConnection *connection, const char *path,
279                                 DBusMessage *message, void *user_data)
280 {
281         struct connman_device *device = user_data;
282         const char *node = g_basename(path);
283         struct connman_network *network;
284
285         DBG("path %s", path);
286
287         network = connman_device_get_network(device, node);
288         if (network != NULL)
289                 return;
290
291         network = connman_network_create(node, CONNMAN_NETWORK_TYPE_WIFI);
292         if (network == NULL)
293                 return;
294
295         connman_device_add_network(device, network);
296 }
297
298 static void check_devices(struct connman_device *adapter,
299                         DBusConnection *connection, DBusMessageIter *array)
300 {
301         DBusMessageIter value;
302
303         if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
304                 return;
305
306         dbus_message_iter_recurse(array, &value);
307
308         while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_OBJECT_PATH) {
309                 const char *path;
310
311                 dbus_message_iter_get_basic(&value, &path);
312
313                 get_properties(connection, path, BLUEZ_DEVICE_INTERFACE,
314                                                 device_properties, adapter);
315
316                 dbus_message_iter_next(&value);
317         }
318 }
319
320 static void property_changed(DBusConnection *connection, DBusMessage *message)
321 {
322         const char *path = dbus_message_get_path(message);
323         struct connman_device *adapter;
324         DBusMessageIter iter, value;
325         const char *key;
326
327         DBG("path %s", path);
328
329         adapter = find_adapter(path);
330         if (adapter == NULL)
331                 return;
332
333         if (dbus_message_iter_init(message, &iter) == FALSE)
334                 return;
335
336         dbus_message_iter_get_basic(&iter, &key);
337
338         dbus_message_iter_next(&iter);
339         dbus_message_iter_recurse(&iter, &value);
340
341         if (g_str_equal(key, "Powered") == TRUE) {
342                 gboolean val;
343
344                 dbus_message_iter_get_basic(&value, &val);
345                 connman_device_set_powered(adapter, val);
346         } else if (g_str_equal(key, "Discovering") == TRUE) {
347                 gboolean val;
348
349                 dbus_message_iter_get_basic(&value, &val);
350                 connman_device_set_scanning(adapter, val);
351         }
352 }
353
354 static void parse_adapter_properties(struct connman_device *adapter,
355                                                 DBusConnection *connection,
356                                                         DBusMessage *reply)
357 {
358         DBusMessageIter array, dict;
359
360         if (dbus_message_iter_init(reply, &array) == FALSE)
361                 return;
362
363         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
364                 return;
365
366         dbus_message_iter_recurse(&array, &dict);
367
368         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
369                 DBusMessageIter entry, value;
370                 const char *key;
371
372                 dbus_message_iter_recurse(&dict, &entry);
373                 dbus_message_iter_get_basic(&entry, &key);
374
375                 dbus_message_iter_next(&entry);
376                 dbus_message_iter_recurse(&entry, &value);
377
378                 if (g_str_equal(key, "Powered") == TRUE) {
379                         gboolean val;
380
381                         dbus_message_iter_get_basic(&value, &val);
382                         connman_device_set_powered(adapter, val);
383                 } else if (g_str_equal(key, "Discovering") == TRUE) {
384                         gboolean val;
385
386                         dbus_message_iter_get_basic(&value, &val);
387                         connman_device_set_scanning(adapter, val);
388                 } else if (g_str_equal(key, "Devices") == TRUE) {
389                         check_devices(adapter, connection, &value);
390                 }
391
392                 dbus_message_iter_next(&dict);
393         }
394 }
395
396 static void adapter_properties(DBusConnection *connection, const char *path,
397                                 DBusMessage *message, void *user_data)
398 {
399         const char *node = g_basename(path);
400         struct connman_device *adapter;
401
402         DBG("path %s", path);
403
404         adapter = find_adapter(path);
405         if (adapter != NULL)
406                 goto done;
407
408         adapter = connman_device_create(node, CONNMAN_DEVICE_TYPE_BLUETOOTH);
409         if (adapter == NULL)
410                 return;
411
412         connman_device_set_path(adapter, path);
413
414         if (g_str_has_prefix(node, "hci") == TRUE) {
415                 int index;
416                 errno = 0;
417                 index = atoi(node + 3);
418                 if (errno == 0)
419                         connman_device_set_index(adapter, index);
420         }
421
422         connman_device_set_interface(adapter, node);
423
424         connman_device_set_mode(adapter, CONNMAN_DEVICE_MODE_MULTIPLE_NETWORKS);
425
426         if (connman_device_register(adapter) < 0) {
427                 connman_device_unref(adapter);
428                 return;
429         }
430
431         adapter_list = g_slist_append(adapter_list, adapter);
432
433 done:
434         parse_adapter_properties(adapter, connection, message);
435 }
436
437 static void add_adapter(DBusConnection *connection, const char *path)
438 {
439         DBG("path %s", path);
440
441         get_properties(connection, path, BLUEZ_ADAPTER_INTERFACE,
442                                                 adapter_properties, NULL);
443 }
444
445 static void remove_adapter(DBusConnection *connection, const char *path)
446 {
447         struct connman_device *adapter;
448
449         DBG("path %s", path);
450
451         adapter = find_adapter(path);
452         if (adapter == NULL)
453                 return;
454
455         adapter_list = g_slist_remove(adapter_list, adapter);
456
457         connman_device_unregister(adapter);
458         connman_device_unref(adapter);
459 }
460
461 static void list_adapters_reply(DBusPendingCall *call, void *user_data)
462 {
463         DBusConnection *connection = user_data;
464         DBusMessage *reply;
465         DBusError error;
466         char **adapters;
467         int i, num_adapters;
468
469         DBG("");
470
471         reply = dbus_pending_call_steal_reply(call);
472
473         dbus_error_init(&error);
474
475         if (dbus_message_get_args(reply, &error,
476                                 DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
477                                                 &adapters, &num_adapters,
478                                                 DBUS_TYPE_INVALID) == FALSE) {
479                 if (dbus_error_is_set(&error) == TRUE) {
480                         connman_error("%s", error.message);
481                         dbus_error_free(&error);
482                 } else
483                         connman_error("Wrong arguments for adapter list");
484                 goto done;
485         }
486
487         for (i = 0; i < num_adapters; i++)
488                 get_properties(connection, adapters[i],
489                                         BLUEZ_ADAPTER_INTERFACE,
490                                                 adapter_properties, NULL);
491
492         g_strfreev(adapters);
493
494 done:
495         dbus_message_unref(reply);
496 }
497
498 static void bluetooth_connect(DBusConnection *connection, void *user_data)
499 {
500         DBusMessage *message;
501         DBusPendingCall *call;
502
503         DBG("connection %p", connection);
504
505         message = dbus_message_new_method_call(BLUEZ_SERVICE, "/",
506                                 BLUEZ_MANAGER_INTERFACE, LIST_ADAPTERS);
507         if (message == NULL)
508                 return;
509
510         if (dbus_connection_send_with_reply(connection, message,
511                                                 &call, TIMEOUT) == FALSE) {
512                 connman_error("Failed to get Bluetooth adapters");
513                 dbus_message_unref(message);
514                 return;
515         }
516
517         dbus_pending_call_set_notify(call, list_adapters_reply,
518                                                         connection, NULL);
519
520         dbus_message_unref(message);
521 }
522
523 static void bluetooth_disconnect(DBusConnection *connection, void *user_data)
524 {
525         DBG("connection %p", connection);
526
527         free_adapters();
528 }
529
530 static DBusHandlerResult bluetooth_signal(DBusConnection *connection,
531                                         DBusMessage *message, void *user_data)
532 {
533         if (dbus_message_has_interface(message,
534                         BLUEZ_MANAGER_INTERFACE) == FALSE &&
535                                 dbus_message_has_interface(message,
536                                         BLUEZ_ADAPTER_INTERFACE) == FALSE)
537                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
538
539         DBG("connection %p", connection);
540
541         if (dbus_message_is_signal(message, BLUEZ_ADAPTER_INTERFACE,
542                                                 PROPERTY_CHANGED) == TRUE) {
543                 property_changed(connection, message);
544         } else if (dbus_message_is_signal(message, BLUEZ_MANAGER_INTERFACE,
545                                                 ADAPTER_ADDED) == TRUE) {
546                 const char *path;
547                 dbus_message_get_args(message, NULL,
548                                         DBUS_TYPE_OBJECT_PATH, &path,
549                                                         DBUS_TYPE_INVALID);
550                 add_adapter(connection, path);
551         } else if (dbus_message_is_signal(message, BLUEZ_MANAGER_INTERFACE,
552                                                 ADAPTER_REMOVED) == TRUE) {
553                 const char *path;
554                 dbus_message_get_args(message, NULL,
555                                         DBUS_TYPE_OBJECT_PATH, &path,
556                                                         DBUS_TYPE_INVALID);
557                 remove_adapter(connection, path);
558         }
559
560         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
561 }
562
563 static DBusConnection *connection;
564 static guint watch;
565
566 static const char *added_rule = "type=signal,member=" ADAPTER_ADDED
567                                         ",interface=" BLUEZ_MANAGER_INTERFACE;
568 static const char *removed_rule = "type=signal,member=" ADAPTER_REMOVED
569                                         ",interface=" BLUEZ_MANAGER_INTERFACE;
570
571 static const char *adapter_rule = "type=signal,member=" PROPERTY_CHANGED
572                                         ",interface=" BLUEZ_ADAPTER_INTERFACE;
573
574 static int bluetooth_init(void)
575 {
576         int err = -EIO;
577
578         connection = connman_dbus_get_connection();
579         if (connection == NULL)
580                 return -EIO;
581
582         if (dbus_connection_add_filter(connection, bluetooth_signal,
583                                                         NULL, NULL) == FALSE)
584                 goto unref;
585
586         err = connman_device_driver_register(&bluetooth_driver);
587         if (err < 0)
588                 goto remove;
589
590         watch = g_dbus_add_service_watch(connection, BLUEZ_SERVICE,
591                         bluetooth_connect, bluetooth_disconnect, NULL, NULL);
592         if (watch == 0) {
593                 connman_device_driver_unregister(&bluetooth_driver);
594                 err = -EIO;
595                 goto remove;
596         }
597
598         if (g_dbus_check_service(connection, BLUEZ_SERVICE) == TRUE)
599                 bluetooth_connect(connection, NULL);
600
601         dbus_bus_add_match(connection, added_rule, NULL);
602         dbus_bus_add_match(connection, removed_rule, NULL);
603         dbus_bus_add_match(connection, adapter_rule, NULL);
604         dbus_connection_flush(connection);
605
606         return 0;
607
608 remove:
609         dbus_connection_remove_filter(connection, bluetooth_signal, NULL);
610
611 unref:
612         dbus_connection_unref(connection);
613
614         return err;
615 }
616
617 static void bluetooth_exit(void)
618 {
619         dbus_bus_remove_match(connection, adapter_rule, NULL);
620         dbus_bus_remove_match(connection, removed_rule, NULL);
621         dbus_bus_remove_match(connection, added_rule, NULL);
622         dbus_connection_flush(connection);
623
624         g_dbus_remove_watch(connection, watch);
625
626         free_adapters();
627
628         connman_device_driver_unregister(&bluetooth_driver);
629
630         dbus_connection_remove_filter(connection, bluetooth_signal, NULL);
631
632         dbus_connection_unref(connection);
633 }
634
635 CONNMAN_PLUGIN_DEFINE(bluetooth, "Bluetooth technology plugin", VERSION,
636                                                 bluetooth_init, bluetooth_exit)