2bc32c3e1a58bccf0d3d9cfd4e0ace31c34dd834
[platform/upstream/connman.git] / plugins / bluetooth.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2007-2010  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 <stdio.h>
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <netinet/ether.h>
31
32 #include <gdbus.h>
33
34 #define CONNMAN_API_SUBJECT_TO_CHANGE
35 #include <connman/plugin.h>
36 #include <connman/technology.h>
37 #include <connman/device.h>
38 #include <connman/inet.h>
39 #include <connman/dbus.h>
40 #include <connman/log.h>
41
42 #define BLUEZ_SERVICE                   "org.bluez"
43 #define BLUEZ_MANAGER_INTERFACE         BLUEZ_SERVICE ".Manager"
44 #define BLUEZ_ADAPTER_INTERFACE         BLUEZ_SERVICE ".Adapter"
45 #define BLUEZ_DEVICE_INTERFACE          BLUEZ_SERVICE ".Device"
46 #define BLUEZ_NETWORK_INTERFACE         BLUEZ_SERVICE ".Network"
47
48 #define LIST_ADAPTERS                   "ListAdapters"
49 #define ADAPTER_ADDED                   "AdapterAdded"
50 #define ADAPTER_REMOVED                 "AdapterRemoved"
51
52 #define PROPERTY_CHANGED                "PropertyChanged"
53 #define GET_PROPERTIES                  "GetProperties"
54 #define SET_PROPERTY                    "SetProperty"
55
56 #define CONNECT                         "Connect"
57 #define DISCONNECT                      "Disconnect"
58
59 #define UUID_NAP        "00001116-0000-1000-8000-00805f9b34fb"
60
61 #define TIMEOUT 5000
62
63 static DBusConnection *connection;
64
65 static GHashTable *bluetooth_devices = NULL;
66 static GHashTable *bluetooth_networks = NULL;
67
68 static int pan_probe(struct connman_network *network)
69 {
70         DBG("network %p", network);
71
72         return 0;
73 }
74
75 static void pan_remove(struct connman_network *network)
76 {
77         DBG("network %p", network);
78 }
79
80 static void connect_reply(DBusPendingCall *call, void *user_data)
81 {
82         struct connman_network *network = user_data;
83         DBusMessage *reply;
84         DBusError error;
85         const char *interface = NULL;
86         int index;
87
88         DBG("network %p", network);
89
90         reply = dbus_pending_call_steal_reply(call);
91
92         dbus_error_init(&error);
93
94         if (dbus_set_error_from_message(&error, reply) == TRUE) {
95                 connman_error("%s", error.message);
96                 dbus_error_free(&error);
97                 goto done;
98         }
99
100         if (dbus_message_get_args(reply, &error,
101                                         DBUS_TYPE_STRING, &interface,
102                                                 DBUS_TYPE_INVALID) == FALSE) {
103                 if (dbus_error_is_set(&error) == TRUE) {
104                         connman_error("%s", error.message);
105                         dbus_error_free(&error);
106                 } else
107                         connman_error("Wrong arguments for connect");
108                 goto done;
109         }
110
111         if (interface == NULL)
112                 goto done;
113
114         DBG("interface %s", interface);
115
116         index = connman_inet_ifindex(interface);
117
118         connman_network_set_index(network, index);
119
120         connman_network_set_connected(network, TRUE);
121
122 done:
123         dbus_message_unref(reply);
124
125         dbus_pending_call_unref(call);
126 }
127
128 static int pan_connect(struct connman_network *network)
129 {
130         const char *path = connman_network_get_string(network, "Path");
131         const char *uuid = "nap";
132         DBusMessage *message;
133         DBusPendingCall *call;
134
135         DBG("network %p", network);
136
137         if (path == NULL)
138                 return -EINVAL;
139
140         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
141                                         BLUEZ_NETWORK_INTERFACE, CONNECT);
142         if (message == NULL)
143                 return -ENOMEM;
144
145         dbus_message_set_auto_start(message, FALSE);
146
147         dbus_message_append_args(message, DBUS_TYPE_STRING, &uuid,
148                                                         DBUS_TYPE_INVALID);
149
150         if (dbus_connection_send_with_reply(connection, message,
151                                         &call, TIMEOUT * 10) == FALSE) {
152                 connman_error("Failed to connect service");
153                 dbus_message_unref(message);
154                 return -EINVAL;
155         }
156
157         if (call == NULL) {
158                 connman_error("D-Bus connection not available");
159                 dbus_message_unref(message);
160                 return -EINVAL;
161         }
162
163         dbus_pending_call_set_notify(call, connect_reply, network, NULL);
164
165         dbus_message_unref(message);
166
167         return -EINPROGRESS;
168 }
169
170 static void disconnect_reply(DBusPendingCall *call, void *user_data)
171 {
172         struct connman_network *network = user_data;
173         DBusMessage *reply;
174         DBusError error;
175
176         DBG("network %p", network);
177
178         reply = dbus_pending_call_steal_reply(call);
179
180         dbus_error_init(&error);
181
182         if (dbus_set_error_from_message(&error, reply) == TRUE) {
183                 connman_error("%s", error.message);
184                 dbus_error_free(&error);
185                 goto done;
186         }
187
188         if (dbus_message_get_args(reply, &error, DBUS_TYPE_INVALID) == FALSE) {
189                 if (dbus_error_is_set(&error) == TRUE) {
190                         connman_error("%s", error.message);
191                         dbus_error_free(&error);
192                 } else
193                         connman_error("Wrong arguments for disconnect");
194                 goto done;
195         }
196
197         connman_network_set_connected(network, FALSE);
198
199 done:
200         dbus_message_unref(reply);
201
202         dbus_pending_call_unref(call);
203
204         connman_network_unref(network);
205 }
206
207 static int pan_disconnect(struct connman_network *network)
208 {
209         const char *path = connman_network_get_string(network, "Path");
210         DBusMessage *message;
211         DBusPendingCall *call;
212
213         DBG("network %p", network);
214
215         if (path == NULL)
216                 return -EINVAL;
217
218         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
219                                         BLUEZ_NETWORK_INTERFACE, DISCONNECT);
220         if (message == NULL)
221                 return -ENOMEM;
222
223         dbus_message_set_auto_start(message, FALSE);
224
225         dbus_message_append_args(message, DBUS_TYPE_INVALID);
226
227         if (dbus_connection_send_with_reply(connection, message,
228                                                 &call, TIMEOUT) == FALSE) {
229                 connman_error("Failed to disconnect service");
230                 dbus_message_unref(message);
231                 return -EINVAL;
232         }
233
234         if (call == NULL) {
235                 connman_error("D-Bus connection not available");
236                 dbus_message_unref(message);
237                 return -EINVAL;
238         }
239
240         connman_network_ref(network);
241
242         connman_network_set_associating(network, FALSE);
243
244         dbus_pending_call_set_notify(call, disconnect_reply, network, NULL);
245
246         dbus_message_unref(message);
247
248         return 0;
249 }
250
251 static struct connman_network_driver pan_driver = {
252         .name           = "bluetooth-pan",
253         .type           = CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN,
254         .probe          = pan_probe,
255         .remove         = pan_remove,
256         .connect        = pan_connect,
257         .disconnect     = pan_disconnect,
258 };
259
260 static gboolean network_changed(DBusConnection *connection,
261                                 DBusMessage *message, void *user_data)
262 {
263         const char *path = dbus_message_get_path(message);
264         struct connman_network *network;
265         DBusMessageIter iter, value;
266         const char *key;
267
268         DBG("path %s", path);
269
270         network = g_hash_table_lookup(bluetooth_networks, path);
271         if (network == NULL)
272                 return TRUE;
273
274         if (dbus_message_iter_init(message, &iter) == FALSE)
275                 return TRUE;
276
277         dbus_message_iter_get_basic(&iter, &key);
278
279         dbus_message_iter_next(&iter);
280         dbus_message_iter_recurse(&iter, &value);
281
282         if (g_str_equal(key, "Connected") == TRUE) {
283                 dbus_bool_t connected;
284
285                 dbus_message_iter_get_basic(&value, &connected);
286
287                 if (connected == TRUE)
288                         return TRUE;
289
290                 connman_network_set_associating(network, FALSE);
291                 connman_network_set_connected(network, FALSE);
292         }
293
294         return TRUE;
295 }
296
297 static void extract_properties(DBusMessage *reply, const char **parent,
298                                                 const char **address,
299                                                 const char **name,
300                                                 const char **alias,
301                                                 dbus_bool_t *powered,
302                                                 dbus_bool_t *scanning,
303                                                 DBusMessageIter *uuids,
304                                                 DBusMessageIter *networks)
305 {
306         DBusMessageIter array, dict;
307
308         if (dbus_message_iter_init(reply, &array) == FALSE)
309                 return;
310
311         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
312                 return;
313
314         dbus_message_iter_recurse(&array, &dict);
315
316         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
317                 DBusMessageIter entry, value;
318                 const char *key;
319
320                 dbus_message_iter_recurse(&dict, &entry);
321                 dbus_message_iter_get_basic(&entry, &key);
322
323                 dbus_message_iter_next(&entry);
324                 dbus_message_iter_recurse(&entry, &value);
325
326                 if (g_str_equal(key, "Adapter") == TRUE) {
327                         if (parent != NULL)
328                                 dbus_message_iter_get_basic(&value, parent);
329                 } else if (g_str_equal(key, "Address") == TRUE) {
330                         if (address != NULL)
331                                 dbus_message_iter_get_basic(&value, address);
332                 } else if (g_str_equal(key, "Name") == TRUE) {
333                         if (name != NULL)
334                                 dbus_message_iter_get_basic(&value, name);
335                 } else if (g_str_equal(key, "Alias") == TRUE) {
336                         if (alias != NULL)
337                                 dbus_message_iter_get_basic(&value, alias);
338                 } else if (g_str_equal(key, "Powered") == TRUE) {
339                         if (powered != NULL)
340                                 dbus_message_iter_get_basic(&value, powered);
341                 } else if (g_str_equal(key, "Discovering") == TRUE) {
342                         if (scanning != NULL)
343                                 dbus_message_iter_get_basic(&value, scanning);
344                 } else if (g_str_equal(key, "Devices") == TRUE) {
345                         if (networks != NULL)
346                                 memcpy(networks, &value, sizeof(value));
347                 } else if (g_str_equal(key, "UUIDs") == TRUE) {
348                         if (uuids != NULL)
349                                 memcpy(uuids, &value, sizeof(value));
350                 }
351
352                 dbus_message_iter_next(&dict);
353         }
354 }
355
356 static dbus_bool_t has_pan(DBusMessageIter *array)
357 {
358         DBusMessageIter value;
359
360         if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
361                 return FALSE;
362
363         dbus_message_iter_recurse(array, &value);
364
365         while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_STRING) {
366                 const char *uuid;
367
368                 dbus_message_iter_get_basic(&value, &uuid);
369
370                 if (g_strcmp0(uuid, UUID_NAP) == 0)
371                         return TRUE;
372
373                 dbus_message_iter_next(&value);
374         }
375
376         return FALSE;
377 }
378
379 static void network_properties_reply(DBusPendingCall *call, void *user_data)
380 {
381         char *path = user_data;
382         struct connman_device *device;
383         struct connman_network *network;
384         DBusMessage *reply;
385         DBusMessageIter uuids;
386         const char *parent = NULL, *address = NULL, *name = NULL;
387         struct ether_addr addr;
388         char ident[13];
389
390         reply = dbus_pending_call_steal_reply(call);
391
392         extract_properties(reply, &parent, &address, NULL, &name,
393                                                 NULL, NULL, &uuids, NULL);
394
395         if (parent == NULL)
396                 goto done;
397
398         device = g_hash_table_lookup(bluetooth_devices, parent);
399         if (device == NULL)
400                 goto done;
401
402         if (address == NULL)
403                 goto done;
404
405         ether_aton_r(address, &addr);
406
407         snprintf(ident, 13, "%02x%02x%02x%02x%02x%02x",
408                                                 addr.ether_addr_octet[0],
409                                                 addr.ether_addr_octet[1],
410                                                 addr.ether_addr_octet[2],
411                                                 addr.ether_addr_octet[3],
412                                                 addr.ether_addr_octet[4],
413                                                 addr.ether_addr_octet[5]);
414
415         if (has_pan(&uuids) == FALSE)
416                 goto done;
417
418         network = connman_device_get_network(device, ident);
419         if (network != NULL)
420                 goto done;
421
422         network = connman_network_create(ident,
423                                         CONNMAN_NETWORK_TYPE_BLUETOOTH_PAN);
424         if (network == NULL)
425                 goto done;
426
427         connman_network_set_string(network, "Path", path);
428
429         connman_network_set_protocol(network, CONNMAN_NETWORK_PROTOCOL_IP);
430
431         connman_network_set_name(network, name);
432
433         connman_device_add_network(device, network);
434
435         connman_network_set_group(network, ident);
436
437         g_hash_table_insert(bluetooth_networks, g_strdup(path), network);
438
439 done:
440         dbus_message_unref(reply);
441
442         dbus_pending_call_unref(call);
443 }
444
445 static void add_network(struct connman_device *device, const char *path)
446 {
447         DBusMessage *message;
448         DBusPendingCall *call;
449
450         DBG("path %s", path);
451
452         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
453                                 BLUEZ_DEVICE_INTERFACE, GET_PROPERTIES);
454         if (message == NULL)
455                 return;
456
457         dbus_message_set_auto_start(message, FALSE);
458
459         if (dbus_connection_send_with_reply(connection, message,
460                                                 &call, TIMEOUT) == FALSE) {
461                 connman_error("Failed to get network properties for %s", path);
462                 goto done;
463         }
464
465         if (call == NULL) {
466                 connman_error("D-Bus connection not available");
467                 goto done;
468         }
469
470         dbus_pending_call_set_notify(call, network_properties_reply,
471                                                 g_strdup(path), g_free);
472
473 done:
474         dbus_message_unref(message);
475 }
476
477 static void check_networks(struct connman_device *device,
478                                                 DBusMessageIter *array)
479 {
480         DBusMessageIter value;
481
482         if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
483                 return;
484
485         dbus_message_iter_recurse(array, &value);
486
487         while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_OBJECT_PATH) {
488                 const char *path;
489
490                 dbus_message_iter_get_basic(&value, &path);
491
492                 add_network(device, path);
493
494                 dbus_message_iter_next(&value);
495         }
496 }
497
498 static gboolean adapter_changed(DBusConnection *connection,
499                                 DBusMessage *message, void *user_data)
500 {
501         const char *path = dbus_message_get_path(message);
502         struct connman_device *device;
503         DBusMessageIter iter, value;
504         const char *key;
505
506         DBG("path %s", path);
507
508         device = g_hash_table_lookup(bluetooth_devices, path);
509         if (device == NULL)
510                 return TRUE;
511
512         if (dbus_message_iter_init(message, &iter) == FALSE)
513                 return TRUE;
514
515         dbus_message_iter_get_basic(&iter, &key);
516
517         dbus_message_iter_next(&iter);
518         dbus_message_iter_recurse(&iter, &value);
519
520         if (g_str_equal(key, "Powered") == TRUE) {
521                 dbus_bool_t val;
522
523                 dbus_message_iter_get_basic(&value, &val);
524                 connman_device_set_powered(device, val);
525         } else if (g_str_equal(key, "Discovering") == TRUE) {
526                 dbus_bool_t val;
527
528                 dbus_message_iter_get_basic(&value, &val);
529                 connman_device_set_scanning(device, val);
530         } else if (g_str_equal(key, "Devices") == TRUE) {
531                 check_networks(device, &value);
532         }
533
534         return TRUE;
535 }
536
537 static gboolean device_changed(DBusConnection *connection,
538                                 DBusMessage *message, void *user_data)
539 {
540         const char *path = dbus_message_get_path(message);
541         DBusMessageIter iter, value;
542         const char *key;
543
544         DBG("path %s", path);
545
546         if (dbus_message_iter_init(message, &iter) == FALSE)
547                 return TRUE;
548
549         dbus_message_iter_get_basic(&iter, &key);
550
551         dbus_message_iter_next(&iter);
552         dbus_message_iter_recurse(&iter, &value);
553
554         DBG("key %s", key);
555
556         if (g_str_equal(key, "UUIDs") == TRUE)
557                 add_network(NULL, path);
558
559         return TRUE;
560 }
561
562 static void adapter_properties_reply(DBusPendingCall *call, void *user_data)
563 {
564         char *path = user_data;
565         struct connman_device *device;
566         DBusMessage *reply;
567         DBusMessageIter networks;
568         const char *address = NULL, *name = NULL;
569         dbus_bool_t powered = FALSE, scanning = FALSE;
570         struct ether_addr addr;
571         char ident[13];
572
573         DBG("path %s", path);
574
575         reply = dbus_pending_call_steal_reply(call);
576
577         if (path == NULL)
578                 goto done;
579
580         extract_properties(reply, NULL, &address, &name, NULL,
581                                         &powered, &scanning, NULL, &networks);
582
583         if (address == NULL)
584                 goto done;
585
586         device = g_hash_table_lookup(bluetooth_devices, path);
587         if (device != NULL)
588                 goto update;
589
590         ether_aton_r(address, &addr);
591
592         snprintf(ident, 13, "%02x%02x%02x%02x%02x%02x",
593                                                 addr.ether_addr_octet[0],
594                                                 addr.ether_addr_octet[1],
595                                                 addr.ether_addr_octet[2],
596                                                 addr.ether_addr_octet[3],
597                                                 addr.ether_addr_octet[4],
598                                                 addr.ether_addr_octet[5]);
599
600         device = connman_device_create(ident, CONNMAN_DEVICE_TYPE_BLUETOOTH);
601         if (device == NULL)
602                 goto done;
603
604         connman_device_set_ident(device, ident);
605
606         connman_device_set_mode(device, CONNMAN_DEVICE_MODE_NETWORK_MULTIPLE);
607
608         connman_device_set_string(device, "Path", path);
609
610         if (connman_device_register(device) < 0) {
611                 connman_device_unref(device);
612                 goto done;
613         }
614
615         g_hash_table_insert(bluetooth_devices, g_strdup(path), device);
616
617 update:
618         connman_device_set_string(device, "Address", address);
619         connman_device_set_string(device, "Name", name);
620         connman_device_set_string(device, "Path", path);
621
622         connman_device_set_powered(device, powered);
623         connman_device_set_scanning(device, scanning);
624
625         if (powered == TRUE)
626                 check_networks(device, &networks);
627
628 done:
629         dbus_message_unref(reply);
630
631         dbus_pending_call_unref(call);
632 }
633
634 static void add_adapter(DBusConnection *connection, const char *path)
635 {
636         DBusMessage *message;
637         DBusPendingCall *call;
638
639         DBG("path %s", path);
640
641         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
642                                 BLUEZ_ADAPTER_INTERFACE, GET_PROPERTIES);
643         if (message == NULL)
644                 return;
645
646         dbus_message_set_auto_start(message, FALSE);
647
648         if (dbus_connection_send_with_reply(connection, message,
649                                                 &call, TIMEOUT) == FALSE) {
650                 connman_error("Failed to get adapter properties for %s", path);
651                 goto done;
652         }
653
654         if (call == NULL) {
655                 connman_error("D-Bus connection not available");
656                 goto done;
657         }
658
659         dbus_pending_call_set_notify(call, adapter_properties_reply,
660                                                 g_strdup(path), g_free);
661
662 done:
663         dbus_message_unref(message);
664 }
665
666 static gboolean adapter_added(DBusConnection *connection, DBusMessage *message,
667                                 void *user_data)
668 {
669         const char *path;
670
671         dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
672                                 DBUS_TYPE_INVALID);
673         add_adapter(connection, path);
674         return TRUE;
675 }
676
677 static void remove_adapter(DBusConnection *connection, const char *path)
678 {
679         DBG("path %s", path);
680
681         g_hash_table_remove(bluetooth_devices, path);
682 }
683
684 static gboolean adapter_removed(DBusConnection *connection, DBusMessage *message,
685                                 void *user_data)
686 {
687         const char *path;
688
689         dbus_message_get_args(message, NULL, DBUS_TYPE_OBJECT_PATH, &path,
690                                 DBUS_TYPE_INVALID);
691         remove_adapter(connection, path);
692         return TRUE;
693 }
694
695 static void list_adapters_reply(DBusPendingCall *call, void *user_data)
696 {
697         DBusMessage *reply;
698         DBusError error;
699         char **adapters;
700         int i, num_adapters;
701
702         DBG("");
703
704         reply = dbus_pending_call_steal_reply(call);
705
706         dbus_error_init(&error);
707
708         if (dbus_set_error_from_message(&error, reply) == TRUE) {
709                 connman_error("%s", error.message);
710                 dbus_error_free(&error);
711                 goto done;
712         }
713
714         if (dbus_message_get_args(reply, &error,
715                                 DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
716                                                 &adapters, &num_adapters,
717                                                 DBUS_TYPE_INVALID) == FALSE) {
718                 if (dbus_error_is_set(&error) == TRUE) {
719                         connman_error("%s", error.message);
720                         dbus_error_free(&error);
721                 } else
722                         connman_error("Wrong arguments for adapter list");
723                 goto done;
724         }
725
726         for (i = 0; i < num_adapters; i++)
727                 add_adapter(connection, adapters[i]);
728
729         g_strfreev(adapters);
730
731 done:
732         dbus_message_unref(reply);
733
734         dbus_pending_call_unref(call);
735 }
736
737 static void unregister_device(gpointer data)
738 {
739         struct connman_device *device = data;
740
741         DBG("");
742
743         connman_device_unregister(device);
744         connman_device_unref(device);
745 }
746
747 static void bluetooth_connect(DBusConnection *connection, void *user_data)
748 {
749         DBusMessage *message;
750         DBusPendingCall *call;
751
752         DBG("connection %p", connection);
753
754         bluetooth_devices = g_hash_table_new_full(g_str_hash, g_str_equal,
755                                                 g_free, unregister_device);
756
757         bluetooth_networks = g_hash_table_new_full(g_str_hash, g_str_equal,
758                                                 g_free, NULL);
759
760         message = dbus_message_new_method_call(BLUEZ_SERVICE, "/",
761                                 BLUEZ_MANAGER_INTERFACE, LIST_ADAPTERS);
762         if (message == NULL)
763                 return;
764
765         dbus_message_set_auto_start(message, FALSE);
766
767         if (dbus_connection_send_with_reply(connection, message,
768                                                 &call, TIMEOUT) == FALSE) {
769                 connman_error("Failed to get Bluetooth adapters");
770                 goto done;
771         }
772
773         if (call == NULL) {
774                 connman_error("D-Bus connection not available");
775                 goto done;
776         }
777
778         dbus_pending_call_set_notify(call, list_adapters_reply, NULL, NULL);
779
780 done:
781         dbus_message_unref(message);
782 }
783
784 static void bluetooth_disconnect(DBusConnection *connection, void *user_data)
785 {
786         DBG("connection %p", connection);
787
788         if (bluetooth_devices == NULL)
789                 return;
790
791         g_hash_table_destroy(bluetooth_networks);
792         g_hash_table_destroy(bluetooth_devices);
793         bluetooth_devices = NULL;
794 }
795
796 static int bluetooth_probe(struct connman_device *device)
797 {
798         DBG("device %p", device);
799
800         return 0;
801 }
802
803 static void bluetooth_remove(struct connman_device *device)
804 {
805         DBG("device %p", device);
806 }
807
808 static void powered_reply(DBusPendingCall *call, void *user_data)
809 {
810         DBusError error;
811         DBusMessage *reply;
812
813         DBG("");
814
815         reply = dbus_pending_call_steal_reply(call);
816
817         dbus_error_init(&error);
818
819         if (dbus_set_error_from_message(&error, reply) == TRUE) {
820                 connman_error("%s", error.message);
821                 dbus_error_free(&error);
822                 dbus_message_unref(reply);
823                 dbus_pending_call_unref(call);
824                 return;
825         }
826
827         dbus_message_unref(reply);
828         dbus_pending_call_unref(call);
829
830         add_adapter(connection, user_data);
831 }
832
833 static int change_powered(DBusConnection *connection, const char *path,
834                                                         dbus_bool_t powered)
835 {
836         DBusMessage *message;
837         DBusMessageIter iter;
838         DBusPendingCall *call;
839
840         DBG("");
841
842         if (path == NULL)
843                 return -EINVAL;
844
845         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
846                                         BLUEZ_ADAPTER_INTERFACE, SET_PROPERTY);
847         if (message == NULL)
848                 return -ENOMEM;
849
850         dbus_message_set_auto_start(message, FALSE);
851
852         dbus_message_iter_init_append(message, &iter);
853         connman_dbus_property_append_basic(&iter, "Powered",
854                                                 DBUS_TYPE_BOOLEAN, &powered);
855
856         if (dbus_connection_send_with_reply(connection, message,
857                                                 &call, TIMEOUT) == FALSE) {
858                 connman_error("Failed to change Powered property");
859                 dbus_message_unref(message);
860                 return -EINVAL;
861         }
862
863         if (call == NULL) {
864                 connman_error("D-Bus connection not available");
865                 dbus_message_unref(message);
866                 return -EINVAL;
867         }
868
869         dbus_pending_call_set_notify(call, powered_reply,
870                                         g_strdup(path), g_free);
871
872         dbus_message_unref(message);
873
874         return -EINPROGRESS;
875 }
876
877 static int bluetooth_enable(struct connman_device *device)
878 {
879         const char *path = connman_device_get_string(device, "Path");
880
881         DBG("device %p", device);
882
883         return change_powered(connection, path, TRUE);
884 }
885
886 static int bluetooth_disable(struct connman_device *device)
887 {
888         const char *path = connman_device_get_string(device, "Path");
889
890         DBG("device %p", device);
891
892         return change_powered(connection, path, FALSE);
893 }
894
895 static struct connman_device_driver bluetooth_driver = {
896         .name           = "bluetooth",
897         .type           = CONNMAN_DEVICE_TYPE_BLUETOOTH,
898         .probe          = bluetooth_probe,
899         .remove         = bluetooth_remove,
900         .enable         = bluetooth_enable,
901         .disable        = bluetooth_disable,
902 };
903
904 static int tech_probe(struct connman_technology *technology)
905 {
906         return 0;
907 }
908
909 static void tech_remove(struct connman_technology *technology)
910 {
911 }
912
913 static int tech_set_tethering(struct connman_technology *technology,
914                                                 connman_bool_t enabled)
915 {
916         return 0;
917 }
918
919 static struct connman_technology_driver tech_driver = {
920         .name           = "bluetooth",
921         .type           = CONNMAN_SERVICE_TYPE_BLUETOOTH,
922         .probe          = tech_probe,
923         .remove         = tech_remove,
924         .set_tethering  = tech_set_tethering,
925 };
926
927 static guint watch;
928 static guint added_watch;
929 static guint removed_watch;
930 static guint adapter_watch;
931 static guint device_watch;
932 static guint network_watch;
933
934 static int bluetooth_init(void)
935 {
936         int err;
937
938         connection = connman_dbus_get_connection();
939         if (connection == NULL)
940                 return -EIO;
941
942         watch = g_dbus_add_service_watch(connection, BLUEZ_SERVICE,
943                         bluetooth_connect, bluetooth_disconnect, NULL, NULL);
944
945         added_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
946                                                 BLUEZ_MANAGER_INTERFACE,
947                                                 ADAPTER_ADDED, adapter_added,
948                                                 NULL, NULL);
949
950         removed_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
951                                                 BLUEZ_MANAGER_INTERFACE,
952                                                 ADAPTER_REMOVED, adapter_removed,
953                                                 NULL, NULL);
954
955         adapter_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
956                                                 BLUEZ_ADAPTER_INTERFACE,
957                                                 PROPERTY_CHANGED, adapter_changed,
958                                                 NULL, NULL);
959
960         device_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
961                                                 BLUEZ_DEVICE_INTERFACE,
962                                                 PROPERTY_CHANGED, device_changed,
963                                                 NULL, NULL);
964
965         network_watch = g_dbus_add_signal_watch(connection, NULL, NULL,
966                                                 BLUEZ_NETWORK_INTERFACE,
967                                                 PROPERTY_CHANGED, network_changed,
968                                                 NULL, NULL);
969
970         if (watch == 0 || added_watch == 0 || removed_watch == 0
971                         || adapter_watch == 0 || network_watch == 0
972                                 || device_watch == 0) {
973                 err = -EIO;
974                 goto remove;
975         }
976
977         err = connman_network_driver_register(&pan_driver);
978         if (err < 0)
979                 goto remove;
980
981         err = connman_device_driver_register(&bluetooth_driver);
982         if (err < 0) {
983                 connman_network_driver_unregister(&pan_driver);
984                 goto remove;
985         }
986
987         err = connman_technology_driver_register(&tech_driver);
988         if (err < 0) {
989                 connman_device_driver_unregister(&bluetooth_driver);
990                 connman_network_driver_unregister(&pan_driver);
991                 return err;
992         }
993
994         return 0;
995
996 remove:
997         g_dbus_remove_watch(connection, watch);
998         g_dbus_remove_watch(connection, added_watch);
999         g_dbus_remove_watch(connection, removed_watch);
1000         g_dbus_remove_watch(connection, adapter_watch);
1001         g_dbus_remove_watch(connection, device_watch);
1002         g_dbus_remove_watch(connection, network_watch);
1003
1004         dbus_connection_unref(connection);
1005
1006         return err;
1007 }
1008
1009 static void bluetooth_exit(void)
1010 {
1011         g_dbus_remove_watch(connection, watch);
1012         g_dbus_remove_watch(connection, added_watch);
1013         g_dbus_remove_watch(connection, removed_watch);
1014         g_dbus_remove_watch(connection, adapter_watch);
1015         g_dbus_remove_watch(connection, device_watch);
1016         g_dbus_remove_watch(connection, network_watch);
1017
1018         bluetooth_disconnect(connection, NULL);
1019
1020         connman_technology_driver_unregister(&tech_driver);
1021
1022         connman_device_driver_unregister(&bluetooth_driver);
1023         connman_network_driver_unregister(&pan_driver);
1024
1025         dbus_connection_unref(connection);
1026 }
1027
1028 CONNMAN_PLUGIN_DEFINE(bluetooth, "Bluetooth technology plugin", VERSION,
1029                 CONNMAN_PLUGIN_PRIORITY_DEFAULT, bluetooth_init, bluetooth_exit)