Add support for different network modes
[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
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         const char *path = connman_device_get_path(adapter);
135
136         DBG("adapter %p", adapter);
137
138         return change_powered(data->connection, path, TRUE);
139 }
140
141 static int bluetooth_disable(struct connman_device *adapter)
142 {
143         struct adapter_data *data = connman_device_get_data(adapter);
144         const char *path = connman_device_get_path(adapter);
145
146         DBG("adapter %p", adapter);
147
148         return change_powered(data->connection, path, 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 *adapter_list = NULL;
169
170 static void free_adapters(void)
171 {
172         GSList *list;
173
174         DBG("");
175
176         for (list = adapter_list; list; list = list->next) {
177                 struct connman_device *adapter = list->data;
178
179                 connman_device_unregister(adapter);
180                 connman_device_unref(adapter);
181         }
182
183         g_slist_free(adapter_list);
184         adapter_list = NULL;
185 }
186
187 static struct connman_device *find_adapter(const char *path)
188 {
189         GSList *list;
190
191         DBG("path %s", path);
192
193         for (list = adapter_list; list; list = list->next) {
194                 struct connman_device *adapter = list->data;
195                 const char *adapter_path = connman_device_get_path(adapter);
196
197                 if (adapter_path == NULL)
198                         continue;
199
200                 if (g_str_equal(adapter_path, path) == TRUE)
201                         return adapter;
202         }
203
204         return NULL;
205 }
206
207 static void check_devices(struct connman_device *adapter,
208                                                 DBusMessageIter *array)
209 {
210         DBusMessageIter value;
211
212         if (dbus_message_iter_get_arg_type(array) != DBUS_TYPE_ARRAY)
213                 return;
214
215         dbus_message_iter_recurse(array, &value);
216
217         while (dbus_message_iter_get_arg_type(&value) == DBUS_TYPE_OBJECT_PATH) {
218                 const char *path;
219
220                 dbus_message_iter_get_basic(&value, &path);
221
222                 DBG("device %s", path);
223
224                 dbus_message_iter_next(&value);
225         }
226 }
227
228 static void property_changed(DBusConnection *connection, DBusMessage *message)
229 {
230         const char *path = dbus_message_get_path(message);
231         struct connman_device *adapter;
232         DBusMessageIter iter, value;
233         const char *key;
234
235         DBG("path %s", path);
236
237         adapter = find_adapter(path);
238         if (adapter == NULL)
239                 return;
240
241         if (dbus_message_iter_init(message, &iter) == FALSE)
242                 return;
243
244         dbus_message_iter_get_basic(&iter, &key);
245
246         dbus_message_iter_next(&iter);
247         dbus_message_iter_recurse(&iter, &value);
248
249         if (g_str_equal(key, "Powered") == TRUE) {
250                 gboolean val;
251
252                 dbus_message_iter_get_basic(&value, &val);
253                 connman_device_set_powered(adapter, val);
254         } else if (g_str_equal(key, "Discovering") == TRUE) {
255                 gboolean val;
256
257                 dbus_message_iter_get_basic(&value, &val);
258                 connman_device_set_scanning(adapter, val);
259         }
260 }
261
262 static void properties_reply(DBusPendingCall *call, void *user_data)
263 {
264         DBusMessage *message = user_data;
265         const char *path = dbus_message_get_path(message);
266         struct connman_device *adapter;
267         DBusMessageIter array, dict;
268         DBusMessage *reply;
269
270         DBG("path %s", path);
271
272         adapter = find_adapter(path);
273
274         dbus_message_unref(message);
275
276         reply = dbus_pending_call_steal_reply(call);
277
278         if (adapter == NULL)
279                 goto done;
280
281         if (dbus_message_iter_init(reply, &array) == FALSE)
282                 goto done;
283
284         if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_ARRAY)
285                 goto done;
286
287         dbus_message_iter_recurse(&array, &dict);
288         while (dbus_message_iter_get_arg_type(&dict) == DBUS_TYPE_DICT_ENTRY) {
289                 DBusMessageIter entry, value;
290                 const char *key;
291
292                 dbus_message_iter_recurse(&dict, &entry);
293                 dbus_message_iter_get_basic(&entry, &key);
294
295                 dbus_message_iter_next(&entry);
296                 dbus_message_iter_recurse(&entry, &value);
297
298                 if (g_str_equal(key, "Powered") == TRUE) {
299                         gboolean val;
300
301                         dbus_message_iter_get_basic(&value, &val);
302                         connman_device_set_powered(adapter, val);
303                 } else if (g_str_equal(key, "Discovering") == TRUE) {
304                         gboolean val;
305
306                         dbus_message_iter_get_basic(&value, &val);
307                         connman_device_set_scanning(adapter, val);
308                 } else if (g_str_equal(key, "Devices") == TRUE) {
309                         check_devices(adapter, &value);
310                 }
311
312                 dbus_message_iter_next(&dict);
313         }
314
315 done:
316         dbus_message_unref(reply);
317 }
318
319 static void add_adapter(DBusConnection *connection, const char *path)
320 {
321         const char *node = g_basename(path);
322         struct connman_device *adapter;
323         DBusMessage *message;
324         DBusPendingCall *call;
325
326         DBG("path %s", path);
327
328         adapter = find_adapter(path);
329         if (adapter != NULL)
330                 return;
331
332         adapter = connman_device_create(node, CONNMAN_DEVICE_TYPE_BLUETOOTH);
333
334         connman_device_set_path(adapter, path);
335
336         if (g_str_has_prefix(node, "hci") == TRUE) {
337                 int index;
338                 errno = 0;
339                 index = atoi(node + 3);
340                 if (errno == 0)
341                         connman_device_set_index(adapter, index);
342         }
343
344         connman_device_set_interface(adapter, node);
345
346         connman_device_set_mode(adapter, CONNMAN_DEVICE_MODE_MULTIPLE_NETWORKS);
347
348         if (connman_device_register(adapter) < 0) {
349                 connman_device_unref(adapter);
350                 return;
351         }
352
353         adapter_list = g_slist_append(adapter_list, adapter);
354
355         message = dbus_message_new_method_call(BLUEZ_SERVICE, path,
356                                 BLUEZ_ADAPTER_INTERFACE, GET_PROPERTIES);
357         if (message == NULL)
358                 return;
359
360         if (dbus_connection_send_with_reply(connection, message,
361                                                 &call, TIMEOUT) == FALSE) {
362                 connman_error("Failed to get adapter properties");
363                 dbus_message_unref(message);
364                 return;
365         }
366
367         dbus_pending_call_set_notify(call, properties_reply, message, NULL);
368 }
369
370 static void remove_adapter(DBusConnection *connection, const char *path)
371 {
372         struct connman_device *adapter;
373
374         DBG("path %s", path);
375
376         adapter = find_adapter(path);
377         if (adapter == NULL)
378                 return;
379
380         adapter_list = g_slist_remove(adapter_list, adapter);
381
382         connman_device_unregister(adapter);
383         connman_device_unref(adapter);
384 }
385
386 static void adapters_reply(DBusPendingCall *call, void *user_data)
387 {
388         DBusConnection *connection = user_data;
389         DBusMessage *reply;
390         DBusError error;
391         char **adapters;
392         int i, num_adapters;
393
394         DBG("");
395
396         reply = dbus_pending_call_steal_reply(call);
397
398         dbus_error_init(&error);
399
400         if (dbus_message_get_args(reply, &error,
401                                 DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
402                                                 &adapters, &num_adapters,
403                                                 DBUS_TYPE_INVALID) == FALSE) {
404                 if (dbus_error_is_set(&error) == TRUE) {
405                         connman_error("%s", error.message);
406                         dbus_error_free(&error);
407                 } else
408                         connman_error("Wrong arguments for adapter list");
409                 goto done;
410         }
411
412         for (i = 0; i < num_adapters; i++)
413                 add_adapter(connection, adapters[i]);
414
415         g_strfreev(adapters);
416
417 done:
418         dbus_message_unref(reply);
419 }
420
421 static void bluetooth_connect(DBusConnection *connection, void *user_data)
422 {
423         DBusMessage *message;
424         DBusPendingCall *call;
425
426         DBG("connection %p", connection);
427
428         message = dbus_message_new_method_call(BLUEZ_SERVICE, "/",
429                                 BLUEZ_MANAGER_INTERFACE, LIST_ADAPTERS);
430         if (message == NULL)
431                 return;
432
433         if (dbus_connection_send_with_reply(connection, message,
434                                                 &call, TIMEOUT) == FALSE) {
435                 connman_error("Failed to get Bluetooth adapters");
436                 dbus_message_unref(message);
437                 return;
438         }
439
440         dbus_pending_call_set_notify(call, adapters_reply, connection, NULL);
441
442         dbus_message_unref(message);
443 }
444
445 static void bluetooth_disconnect(DBusConnection *connection, void *user_data)
446 {
447         DBG("connection %p", connection);
448
449         free_adapters();
450 }
451
452 static DBusHandlerResult bluetooth_signal(DBusConnection *connection,
453                                         DBusMessage *message, void *user_data)
454 {
455         if (dbus_message_has_interface(message,
456                         BLUEZ_MANAGER_INTERFACE) == FALSE &&
457                                 dbus_message_has_interface(message,
458                                         BLUEZ_ADAPTER_INTERFACE) == FALSE)
459                 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
460
461         DBG("connection %p", connection);
462
463         if (dbus_message_is_signal(message, BLUEZ_ADAPTER_INTERFACE,
464                                                 PROPERTY_CHANGED) == TRUE) {
465                 property_changed(connection, message);
466         } else if (dbus_message_is_signal(message, BLUEZ_MANAGER_INTERFACE,
467                                                 ADAPTER_ADDED) == TRUE) {
468                 const char *path;
469                 dbus_message_get_args(message, NULL,
470                                         DBUS_TYPE_OBJECT_PATH, &path,
471                                                         DBUS_TYPE_INVALID);
472                 add_adapter(connection, path);
473         } else if (dbus_message_is_signal(message, BLUEZ_MANAGER_INTERFACE,
474                                                 ADAPTER_REMOVED) == TRUE) {
475                 const char *path;
476                 dbus_message_get_args(message, NULL,
477                                         DBUS_TYPE_OBJECT_PATH, &path,
478                                                         DBUS_TYPE_INVALID);
479                 remove_adapter(connection, path);
480         }
481
482         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
483 }
484
485 static DBusConnection *connection;
486 static guint watch;
487
488 static const char *added_rule = "type=signal,member=" ADAPTER_ADDED
489                                         ",interface=" BLUEZ_MANAGER_INTERFACE;
490 static const char *removed_rule = "type=signal,member=" ADAPTER_REMOVED
491                                         ",interface=" BLUEZ_MANAGER_INTERFACE;
492
493 static const char *adapter_rule = "type=signal,member=" PROPERTY_CHANGED
494                                         ",interface=" BLUEZ_ADAPTER_INTERFACE;
495
496 static int bluetooth_init(void)
497 {
498         int err = -EIO;
499
500         connection = connman_dbus_get_connection();
501         if (connection == NULL)
502                 return -EIO;
503
504         if (dbus_connection_add_filter(connection, bluetooth_signal,
505                                                         NULL, NULL) == FALSE)
506                 goto unref;
507
508         err = connman_device_driver_register(&bluetooth_driver);
509         if (err < 0)
510                 goto remove;
511
512         watch = g_dbus_add_service_watch(connection, BLUEZ_SERVICE,
513                         bluetooth_connect, bluetooth_disconnect, NULL, NULL);
514         if (watch == 0) {
515                 connman_device_driver_unregister(&bluetooth_driver);
516                 err = -EIO;
517                 goto remove;
518         }
519
520         if (g_dbus_check_service(connection, BLUEZ_SERVICE) == TRUE)
521                 bluetooth_connect(connection, NULL);
522
523         dbus_bus_add_match(connection, added_rule, NULL);
524         dbus_bus_add_match(connection, removed_rule, NULL);
525         dbus_bus_add_match(connection, adapter_rule, NULL);
526         dbus_connection_flush(connection);
527
528         return 0;
529
530 remove:
531         dbus_connection_remove_filter(connection, bluetooth_signal, NULL);
532
533 unref:
534         dbus_connection_unref(connection);
535
536         return err;
537 }
538
539 static void bluetooth_exit(void)
540 {
541         dbus_bus_remove_match(connection, adapter_rule, NULL);
542         dbus_bus_remove_match(connection, removed_rule, NULL);
543         dbus_bus_remove_match(connection, added_rule, NULL);
544         dbus_connection_flush(connection);
545
546         g_dbus_remove_watch(connection, watch);
547
548         free_adapters();
549
550         connman_device_driver_unregister(&bluetooth_driver);
551
552         dbus_connection_remove_filter(connection, bluetooth_signal, NULL);
553
554         dbus_connection_unref(connection);
555 }
556
557 CONNMAN_PLUGIN_DEFINE(bluetooth, "Bluetooth technology plugin", VERSION,
558                                                 bluetooth_init, bluetooth_exit)