Add support dynamic Bluetooth adapter detection
[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/log.h>
33
34 #define BLUEZ_SERVICE                   "org.bluez"
35 #define BLUEZ_MANAGER_INTERFACE         BLUEZ_SERVICE ".Manager"
36 #define BLUEZ_ADAPTER_INTERFACE         BLUEZ_SERVICE ".Adapter"
37
38 #define ADAPTER_ADDED                   "AdapterAdded"
39 #define ADAPTER_REMOVED                 "AdapterRemoved"
40 #define PROPERTY_CHANGED                "PropertyChanged"
41
42 #define TIMEOUT 5000
43
44 static int bluetooth_probe(struct connman_device *device)
45 {
46         DBG("device %p", device);
47
48         return 0;
49 }
50
51 static void bluetooth_remove(struct connman_device *device)
52 {
53         DBG("device %p", device);
54 }
55
56 static struct connman_device_driver bluetooth_driver = {
57         .name   = "bluetooth",
58         .type   = CONNMAN_DEVICE_TYPE_BLUETOOTH,
59         .probe  = bluetooth_probe,
60         .remove = bluetooth_remove,
61 };
62
63 static GSList *device_list = NULL;
64
65 static void property_changed(DBusMessage *msg)
66 {
67         DBG("");
68 }
69
70 static struct connman_element *find_adapter(const char *path)
71 {
72         const char *devname = g_basename(path);
73         GSList *list;
74
75         DBG("path %s", path);
76
77         for (list = device_list; list; list = list->next) {
78                 struct connman_element *device = list->data;
79
80                 if (g_str_equal(device->devname, devname) == TRUE)
81                         return device;
82         }
83
84         return NULL;
85 }
86
87 static void add_adapter(const char *path)
88 {
89         struct connman_element *device;
90
91         DBG("path %s", path);
92
93         device = find_adapter(path);
94         if (device != NULL)
95                 return;
96
97         device = connman_element_create(NULL);
98         device->type = CONNMAN_ELEMENT_TYPE_DEVICE;
99         device->subtype = CONNMAN_ELEMENT_SUBTYPE_BLUETOOTH;
100
101         device->name = g_path_get_basename(path);
102
103         connman_element_register(device, NULL);
104         device_list = g_slist_append(device_list, device);
105 }
106
107 static void remove_adapter(const char *path)
108 {
109         struct connman_element *device;
110
111         DBG("path %s", path);
112
113         device = find_adapter(path);
114         if (device == NULL)
115                 return;
116
117         device_list = g_slist_remove(device_list, device);
118
119         connman_element_unregister(device);
120         connman_element_unref(device);
121 }
122
123 static void adapters_reply(DBusPendingCall *call, void *user_data)
124 {
125         DBusMessage *reply;
126         DBusError error;
127         char **adapters;
128         int i, num_adapters;
129
130         DBG("");
131
132         reply = dbus_pending_call_steal_reply(call);
133
134         dbus_error_init(&error);
135
136         if (dbus_message_get_args(reply, &error,
137                                 DBUS_TYPE_ARRAY, DBUS_TYPE_OBJECT_PATH,
138                                                 &adapters, &num_adapters,
139                                                 DBUS_TYPE_INVALID) == FALSE) {
140                 if (dbus_error_is_set(&error) == TRUE) {
141                         connman_error("%s", error.message);
142                         dbus_error_free(&error);
143                 } else
144                         connman_error("Wrong arguments for adapter list");
145                 goto done;
146         }
147
148         for (i = 0; i < num_adapters; i++)
149                 add_adapter(adapters[i]);
150
151         g_strfreev(adapters);
152
153 done:
154         dbus_message_unref(reply);
155 }
156
157 static void bluetooth_connect(DBusConnection *connection, void *user_data)
158 {
159         DBusMessage *message;
160         DBusPendingCall *call;
161
162         DBG("connection %p", connection);
163
164         message = dbus_message_new_method_call(BLUEZ_SERVICE, "/",
165                                 BLUEZ_MANAGER_INTERFACE, "ListAdapters");
166         if (message == NULL)
167                 return;
168
169         if (dbus_connection_send_with_reply(connection, message,
170                                                 &call, TIMEOUT) == FALSE) {
171                 connman_error("Failed to get Bluetooth adapters");
172                 dbus_message_unref(message);
173                 return;
174         }
175
176         dbus_pending_call_set_notify(call, adapters_reply, NULL, NULL);
177
178         dbus_message_unref(message);
179 }
180
181 static void bluetooth_disconnect(DBusConnection *connection, void *user_data)
182 {
183         GSList *list;
184
185         DBG("connection %p", connection);
186
187         for (list = device_list; list; list = list->next) {
188                 struct connman_element *device = list->data;
189
190                 connman_element_unregister(device);
191                 connman_element_unref(device);
192         }
193
194         g_slist_free(device_list);
195         device_list = NULL;
196 }
197
198 static DBusHandlerResult bluetooth_signal(DBusConnection *conn,
199                                                 DBusMessage *msg, void *data)
200 {
201         DBG("connection %p", conn);
202
203         if (dbus_message_is_signal(msg, BLUEZ_ADAPTER_INTERFACE,
204                                                 PROPERTY_CHANGED) == TRUE) {
205                 property_changed(msg);
206         } else if (dbus_message_is_signal(msg, BLUEZ_MANAGER_INTERFACE,
207                                                 ADAPTER_ADDED) == TRUE) {
208                 const char *path;
209                 dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
210                                                         DBUS_TYPE_INVALID);
211                 add_adapter(path);
212         } else if (dbus_message_is_signal(msg, BLUEZ_MANAGER_INTERFACE,
213                                                 ADAPTER_REMOVED) == TRUE) {
214                 const char *path;
215                 dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
216                                                         DBUS_TYPE_INVALID);
217                 remove_adapter(path);
218         }
219
220         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
221 }
222
223 static DBusConnection *connection;
224 static guint watch;
225
226 static const char *added_rule = "type=signal,member=" ADAPTER_ADDED
227                                         ",interface=" BLUEZ_MANAGER_INTERFACE;
228 static const char *removed_rule = "type=signal,member=" ADAPTER_REMOVED
229                                         ",interface=" BLUEZ_MANAGER_INTERFACE;
230
231 static const char *adapter_rule = "type=signal,member=" PROPERTY_CHANGED
232                                         ",interface=" BLUEZ_ADAPTER_INTERFACE;
233
234 static int bluetooth_init(void)
235 {
236         int err = -EIO;
237
238         connection = dbus_bus_get(DBUS_BUS_SYSTEM, NULL);
239         if (connection == NULL)
240                 return -EIO;
241
242         if (dbus_connection_add_filter(connection, bluetooth_signal,
243                                                         NULL, NULL) == FALSE)
244                 goto unref;
245
246         err = connman_device_driver_register(&bluetooth_driver);
247         if (err < 0)
248                 goto remove;
249
250         watch = g_dbus_add_service_watch(connection, BLUEZ_SERVICE,
251                         bluetooth_connect, bluetooth_disconnect, NULL, NULL);
252         if (watch == 0) {
253                 connman_device_driver_unregister(&bluetooth_driver);
254                 err = -EIO;
255                 goto remove;
256         }
257
258         if (g_dbus_check_service(connection, BLUEZ_SERVICE) == TRUE)
259                 bluetooth_connect(connection, NULL);
260
261         dbus_bus_add_match(connection, added_rule, NULL);
262         dbus_bus_add_match(connection, removed_rule, NULL);
263         dbus_bus_add_match(connection, adapter_rule, NULL);
264         dbus_connection_flush(connection);
265
266         return 0;
267
268 remove:
269         dbus_connection_remove_filter(connection, bluetooth_signal, NULL);
270
271 unref:
272         dbus_connection_unref(connection);
273
274         return err;
275 }
276
277 static void bluetooth_exit(void)
278 {
279         dbus_bus_remove_match(connection, adapter_rule, NULL);
280         dbus_bus_remove_match(connection, removed_rule, NULL);
281         dbus_bus_remove_match(connection, added_rule, NULL);
282         dbus_connection_flush(connection);
283
284         g_dbus_remove_watch(connection, watch);
285
286         bluetooth_disconnect(connection, NULL);
287
288         connman_device_driver_unregister(&bluetooth_driver);
289
290         dbus_connection_remove_filter(connection, bluetooth_signal, NULL);
291
292         dbus_connection_unref(connection);
293 }
294
295 CONNMAN_PLUGIN_DEFINE(bluetooth, "Bluetooth technology plugin", VERSION,
296                                                 bluetooth_init, bluetooth_exit)