]> code.delx.au - pulseaudio/blob - src/modules/bluetooth/module-bluetooth-proximity.c
make distcheck pass
[pulseaudio] / src / modules / bluetooth / module-bluetooth-proximity.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2005-2006 Lennart Poettering
5
6 PulseAudio is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2 of the License,
9 or (at your option) any later version.
10
11 PulseAudio is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with PulseAudio; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
19 USA.
20 ***/
21
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
25
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <signal.h>
31 #include <errno.h>
32 #include <sys/types.h>
33 #include <sys/wait.h>
34
35 #include <pulse/xmalloc.h>
36 #include <pulsecore/module.h>
37 #include <pulsecore/log.h>
38 #include <pulsecore/namereg.h>
39 #include <pulsecore/sink.h>
40 #include <pulsecore/modargs.h>
41 #include <pulsecore/macro.h>
42 #include <pulsecore/core-util.h>
43 #include <pulsecore/core-error.h>
44 #include <pulsecore/start-child.h>
45
46 #include "../dbus-util.h"
47 #include "module-bluetooth-proximity-symdef.h"
48
49 PA_MODULE_AUTHOR("Lennart Poettering");
50 PA_MODULE_DESCRIPTION("Bluetooth Proximity Volume Control");
51 PA_MODULE_VERSION(PACKAGE_VERSION);
52 PA_MODULE_LOAD_ONCE(TRUE);
53 PA_MODULE_USAGE(
54 "sink=<sink name> "
55 "hci=<hci device> "
56 );
57
58 #define DEFAULT_HCI "hci0"
59
60 static const char* const valid_modargs[] = {
61 "sink",
62 "rssi",
63 "hci",
64 NULL,
65 };
66
67 struct bonding {
68 struct userdata *userdata;
69 char address[18];
70
71 pid_t pid;
72 int fd;
73
74 pa_io_event *io_event;
75
76 enum {
77 UNKNOWN,
78 FOUND,
79 NOT_FOUND
80 } state;
81 };
82
83 struct userdata {
84 pa_module *module;
85 pa_dbus_connection *dbus_connection;
86
87 char *sink_name;
88 char *hci, *hci_path;
89
90 pa_hashmap *bondings;
91
92 unsigned n_found;
93 unsigned n_unknown;
94
95 pa_bool_t muted;
96 };
97
98 static void update_volume(struct userdata *u) {
99 pa_assert(u);
100
101 if (u->muted && u->n_found > 0) {
102 pa_sink *s;
103
104 u->muted = FALSE;
105
106 if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, FALSE))) {
107 pa_log_warn("Sink device '%s' not available for unmuting.", pa_strnull(u->sink_name));
108 return;
109 }
110
111 pa_log_info("Found %u BT devices, unmuting.", u->n_found);
112 pa_sink_set_mute(s, FALSE);
113
114 } else if (!u->muted && (u->n_found+u->n_unknown) <= 0) {
115 pa_sink *s;
116
117 u->muted = TRUE;
118
119 if (!(s = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, FALSE))) {
120 pa_log_warn("Sink device '%s' not available for muting.", pa_strnull(u->sink_name));
121 return;
122 }
123
124 pa_log_info("No BT devices found, muting.");
125 pa_sink_set_mute(s, TRUE);
126
127 } else
128 pa_log_info("%u devices now active, %u with unknown state.", u->n_found, u->n_unknown);
129 }
130
131 static void bonding_free(struct bonding *b) {
132 pa_assert(b);
133
134 if (b->state == FOUND)
135 pa_assert_se(b->userdata->n_found-- >= 1);
136
137 if (b->state == UNKNOWN)
138 pa_assert_se(b->userdata->n_unknown-- >= 1);
139
140 if (b->pid != (pid_t) -1) {
141 kill(b->pid, SIGTERM);
142 waitpid(b->pid, NULL, 0);
143 }
144
145 if (b->fd >= 0)
146 pa_close(b->fd);
147
148 if (b->io_event)
149 b->userdata->module->core->mainloop->io_free(b->io_event);
150
151 pa_xfree(b);
152 }
153
154 static void io_event_cb(
155 pa_mainloop_api*a,
156 pa_io_event* e,
157 int fd,
158 pa_io_event_flags_t events,
159 void *userdata) {
160
161 struct bonding *b = userdata;
162 char x;
163 ssize_t r;
164
165 pa_assert(b);
166
167 if ((r = read(fd, &x, 1)) <= 0) {
168 pa_log_warn("Child watching '%s' died abnormally: %s", b->address, r == 0 ? "EOF" : pa_cstrerror(errno));
169
170 pa_assert_se(pa_hashmap_remove(b->userdata->bondings, b->address) == b);
171 bonding_free(b);
172 return;
173 }
174
175 pa_assert_se(r == 1);
176
177 if (b->state == UNKNOWN)
178 pa_assert_se(b->userdata->n_unknown-- >= 1);
179
180 if (x == '+') {
181 pa_assert(b->state == UNKNOWN || b->state == NOT_FOUND);
182
183 b->state = FOUND;
184 b->userdata->n_found++;
185
186 pa_log_info("Device '%s' is alive.", b->address);
187
188 } else {
189 pa_assert(x == '-');
190 pa_assert(b->state == UNKNOWN || b->state == FOUND);
191
192 if (b->state == FOUND)
193 b->userdata->n_found--;
194
195 b->state = NOT_FOUND;
196
197 pa_log_info("Device '%s' is dead.", b->address);
198 }
199
200 update_volume(b->userdata);
201 }
202
203 static struct bonding* bonding_new(struct userdata *u, const char *a) {
204 struct bonding *b = NULL;
205 DBusMessage *m = NULL, *r = NULL;
206 DBusError e;
207 const char *class;
208
209 pa_assert(u);
210 pa_assert(a);
211
212 pa_return_val_if_fail(strlen(a) == 17, NULL);
213 pa_return_val_if_fail(!pa_hashmap_get(u->bondings, a), NULL);
214
215 dbus_error_init(&e);
216
217 pa_assert_se(m = dbus_message_new_method_call("org.bluez", u->hci_path, "org.bluez.Adapter", "GetRemoteMajorClass"));
218 pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID));
219 r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->dbus_connection), m, -1, &e);
220
221 if (!r) {
222 pa_log("org.bluez.Adapter.GetRemoteMajorClass(%s) failed: %s", a, e.message);
223 goto fail;
224 }
225
226 if (!(dbus_message_get_args(r, &e, DBUS_TYPE_STRING, &class, DBUS_TYPE_INVALID))) {
227 pa_log("Malformed org.bluez.Adapter.GetRemoteMajorClass signal: %s", e.message);
228 goto fail;
229 }
230
231 if (strcmp(class, "phone")) {
232 pa_log_info("Found device '%s' of class '%s', ignoring.", a, class);
233 goto fail;
234 }
235
236 b = pa_xnew(struct bonding, 1);
237 b->userdata = u;
238 pa_strlcpy(b->address, a, sizeof(b->address));
239 b->pid = (pid_t) -1;
240 b->fd = -1;
241 b->io_event = NULL;
242 b->state = UNKNOWN;
243 u->n_unknown ++;
244
245 pa_log_info("Watching device '%s' of class '%s'.", b->address, class);
246
247 if ((b->fd = pa_start_child_for_read(PA_BT_PROXIMITY_HELPER, a, &b->pid)) < 0) {
248 pa_log("Failed to start helper tool.");
249 goto fail;
250 }
251
252 b->io_event = u->module->core->mainloop->io_new(
253 u->module->core->mainloop,
254 b->fd,
255 PA_IO_EVENT_INPUT,
256 io_event_cb,
257 b);
258
259 dbus_message_unref(m);
260 dbus_message_unref(r);
261
262 pa_hashmap_put(u->bondings, b->address, b);
263
264 return b;
265
266 fail:
267 if (m)
268 dbus_message_unref(m);
269 if (r)
270 dbus_message_unref(r);
271
272 if (b)
273 bonding_free(b);
274
275 dbus_error_free(&e);
276 return NULL;
277 }
278
279 static void bonding_remove(struct userdata *u, const char *a) {
280 struct bonding *b;
281 pa_assert(u);
282
283 pa_return_if_fail((b = pa_hashmap_remove(u->bondings, a)));
284
285 pa_log_info("No longer watching device '%s'", b->address);
286 bonding_free(b);
287 }
288
289 static DBusHandlerResult filter_func(DBusConnection *connection, DBusMessage *m, void *userdata) {
290 struct userdata *u = userdata;
291 DBusError e;
292
293 dbus_error_init(&e);
294
295 if (dbus_message_is_signal(m, "org.bluez.Adapter", "BondingCreated")) {
296 const char *a;
297
298 if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID))) {
299 pa_log("Malformed org.bluez.Adapter.BondingCreated signal: %s", e.message);
300 goto finish;
301 }
302
303 bonding_new(u, a);
304
305 return DBUS_HANDLER_RESULT_HANDLED;
306
307 } else if (dbus_message_is_signal(m, "org.bluez.Adapter", "BondingRemoved")) {
308
309 const char *a;
310
311 if (!(dbus_message_get_args(m, &e, DBUS_TYPE_STRING, &a, DBUS_TYPE_INVALID))) {
312 pa_log("Malformed org.bluez.Adapter.BondingRemoved signal: %s", e.message);
313 goto finish;
314 }
315
316 bonding_remove(u, a);
317
318 return DBUS_HANDLER_RESULT_HANDLED;
319 }
320
321 finish:
322
323 dbus_error_free(&e);
324
325 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
326 }
327
328 static int add_matches(struct userdata *u, pa_bool_t add) {
329 char *filter1, *filter2;
330 DBusError e;
331 int r = -1;
332
333 pa_assert(u);
334 dbus_error_init(&e);
335
336 filter1 = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='BondingCreated',path='%s'", u->hci_path);
337 filter2 = pa_sprintf_malloc("type='signal',sender='org.bluez',interface='org.bluez.Adapter',member='BondingRemoved',path='%s'", u->hci_path);
338
339 if (add) {
340 dbus_bus_add_match(pa_dbus_connection_get(u->dbus_connection), filter1, &e);
341
342 if (dbus_error_is_set(&e)) {
343 pa_log("dbus_bus_add_match(%s) failed: %s", filter1, e.message);
344 goto finish;
345 }
346 } else
347 dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter1, &e);
348
349
350 if (add) {
351 dbus_bus_add_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
352
353 if (dbus_error_is_set(&e)) {
354 pa_log("dbus_bus_add_match(%s) failed: %s", filter2, e.message);
355 dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
356 goto finish;
357 }
358 } else
359 dbus_bus_remove_match(pa_dbus_connection_get(u->dbus_connection), filter2, &e);
360
361 if (add)
362 pa_assert_se(dbus_connection_add_filter(pa_dbus_connection_get(u->dbus_connection), filter_func, u, NULL));
363 else
364 dbus_connection_remove_filter(pa_dbus_connection_get(u->dbus_connection), filter_func, u);
365
366 r = 0;
367
368 finish:
369 pa_xfree(filter1);
370 pa_xfree(filter2);
371 dbus_error_free(&e);
372
373 return r;
374 }
375
376 int pa__init(pa_module*m) {
377 pa_modargs *ma = NULL;
378 struct userdata *u;
379 DBusError e;
380 DBusMessage *msg = NULL, *r = NULL;
381 DBusMessageIter iter, sub;
382
383 pa_assert(m);
384 dbus_error_init(&e);
385
386 if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
387 pa_log("Failed to parse module arguments");
388 goto fail;
389 }
390
391 m->userdata = u = pa_xnew0(struct userdata, 1);
392 u->module = m;
393 u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
394 u->hci = pa_xstrdup(pa_modargs_get_value(ma, "hci", DEFAULT_HCI));
395 u->hci_path = pa_sprintf_malloc("/org/bluez/%s", u->hci);
396 u->n_found = u->n_unknown = 0;
397 u->muted = FALSE;
398
399 u->bondings = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
400
401 if (!(u->dbus_connection = pa_dbus_bus_get(m->core, DBUS_BUS_SYSTEM, &e))) {
402 pa_log("Failed to get D-Bus connection: %s", e.message);
403 goto fail;
404 }
405
406 if (add_matches(u, TRUE) < 0)
407 goto fail;
408
409 pa_assert_se(msg = dbus_message_new_method_call("org.bluez", u->hci_path, "org.bluez.Adapter", "ListBondings"));
410
411 if (!(r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(u->dbus_connection), msg, -1, &e))) {
412 pa_log("org.bluez.Adapter.ListBondings failed: %s", e.message);
413 goto fail;
414 }
415
416 dbus_message_iter_init(r, &iter);
417
418 if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
419 pa_log("Malformed reply to org.bluez.Adapter.ListBondings.");
420 goto fail;
421 }
422
423 dbus_message_iter_recurse(&iter, &sub);
424
425 while (dbus_message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
426 const char *a = NULL;
427
428 dbus_message_iter_get_basic(&sub, &a);
429 bonding_new(u, a);
430
431 dbus_message_iter_next(&sub);
432 }
433
434 dbus_message_unref(r);
435 dbus_message_unref(msg);
436
437 pa_modargs_free(ma);
438
439 if (pa_hashmap_size(u->bondings) == 0)
440 pa_log_warn("Warning: no phone device bonded.");
441
442 update_volume(u);
443
444 return 0;
445
446 fail:
447
448 if (ma)
449 pa_modargs_free(ma);
450
451 pa__done(m);
452
453 dbus_error_free(&e);
454
455 if (msg)
456 dbus_message_unref(msg);
457
458 if (r)
459 dbus_message_unref(r);
460
461 return -1;
462 }
463
464 void pa__done(pa_module*m) {
465 struct userdata *u;
466 pa_assert(m);
467
468 if (!(u = m->userdata))
469 return;
470
471 if (u->bondings) {
472 struct bonding *b;
473
474 while ((b = pa_hashmap_steal_first(u->bondings)))
475 bonding_free(b);
476
477 pa_hashmap_free(u->bondings, NULL, NULL);
478 }
479
480 if (u->dbus_connection) {
481 add_matches(u, FALSE);
482 pa_dbus_connection_unref(u->dbus_connection);
483 }
484
485 pa_xfree(u->sink_name);
486 pa_xfree(u->hci_path);
487 pa_xfree(u->hci);
488 pa_xfree(u);
489 }