~ubuntu-branches/ubuntu/jaunty/pulseaudio/jaunty-updates

« back to all changes in this revision

Viewing changes to src/modules/module-bt-proximity.c

  • Committer: Bazaar Package Importer
  • Author(s): Daniel T Chen
  • Date: 2007-12-04 00:56:08 UTC
  • mto: This revision was merged to the branch mainline in revision 15.
  • Revision ID: james.westby@ubuntu.com-20071204005608-3lzrrrpxi186kgx4
Tags: upstream-0.9.8
ImportĀ upstreamĀ versionĀ 0.9.8

Show diffs side-by-side

added added

removed removed

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