Add "Rear Mic" to alsa mixer paths.
[profile/ivi/pulseaudio.git] / src / modules / module-bonjour-publish.c
1 /***
2   This file is part of PulseAudio.
3
4   Copyright 2009 Daniel Mack
5   based on module-zeroconf-publish.c
6
7   PulseAudio is free software; you can redistribute it and/or modify
8   it under the terms of the GNU Lesser General Public License as
9   published by the Free Software Foundation; either version 2.1 of the
10   License, or (at your option) any later version.
11
12   PulseAudio is distributed in the hope that it will be useful, but
13   WITHOUT ANY WARRANTY; without even the implied warranty of
14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15   General Public License for more details.
16
17   You should have received a copy of the GNU Lesser General Public
18   License along with PulseAudio; if not, write to the Free Software
19   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20   USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <dns_sd.h>
32
33 #include <CoreFoundation/CoreFoundation.h>
34
35 #include <pulse/xmalloc.h>
36 #include <pulse/util.h>
37
38 #include <pulsecore/parseaddr.h>
39 #include <pulsecore/sink.h>
40 #include <pulsecore/source.h>
41 #include <pulsecore/native-common.h>
42 #include <pulsecore/core-util.h>
43 #include <pulsecore/log.h>
44 #include <pulsecore/core-subscribe.h>
45 #include <pulsecore/dynarray.h>
46 #include <pulsecore/modargs.h>
47 #include <pulsecore/avahi-wrap.h>
48 #include <pulsecore/endianmacros.h>
49 #include <pulsecore/protocol-native.h>
50
51 #include "module-bonjour-publish-symdef.h"
52
53 PA_MODULE_AUTHOR("Daniel Mack");
54 PA_MODULE_DESCRIPTION("Mac OS X Bonjour Service Publisher");
55 PA_MODULE_VERSION(PACKAGE_VERSION);
56 PA_MODULE_LOAD_ONCE(TRUE);
57
58 #define SERVICE_TYPE_SINK "_pulse-sink._tcp"
59 #define SERVICE_TYPE_SOURCE "_pulse-source._tcp"
60 #define SERVICE_TYPE_SERVER "_pulse-server._tcp"
61
62 static const char* const valid_modargs[] = {
63     NULL
64 };
65
66 enum service_subtype {
67     SUBTYPE_HARDWARE,
68     SUBTYPE_VIRTUAL,
69     SUBTYPE_MONITOR
70 };
71
72 struct service {
73     struct userdata *userdata;
74     DNSServiceRef service;
75     DNSRecordRef rec, rec2;
76     char *service_name;
77     pa_object *device;
78     enum service_subtype subtype;
79 };
80
81 struct userdata {
82     pa_core *core;
83     pa_module *module;
84
85     pa_hashmap *services;
86     char *service_name;
87
88     pa_hook_slot *sink_new_slot, *source_new_slot, *sink_unlink_slot, *source_unlink_slot, *sink_changed_slot, *source_changed_slot;
89
90     pa_native_protocol *native;
91     DNSServiceRef main_service;
92 };
93
94 static void get_service_data(struct service *s, pa_sample_spec *ret_ss, pa_channel_map *ret_map, const char **ret_name, pa_proplist **ret_proplist, enum service_subtype *ret_subtype) {
95     pa_assert(s);
96     pa_assert(ret_ss);
97     pa_assert(ret_proplist);
98     pa_assert(ret_subtype);
99
100     if (pa_sink_isinstance(s->device)) {
101         pa_sink *sink = PA_SINK(s->device);
102
103         *ret_ss = sink->sample_spec;
104         *ret_map = sink->channel_map;
105         *ret_name = sink->name;
106         *ret_proplist = sink->proplist;
107         *ret_subtype = sink->flags & PA_SINK_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL;
108
109     } else if (pa_source_isinstance(s->device)) {
110         pa_source *source = PA_SOURCE(s->device);
111
112         *ret_ss = source->sample_spec;
113         *ret_map = source->channel_map;
114         *ret_name = source->name;
115         *ret_proplist = source->proplist;
116         *ret_subtype = source->monitor_of ? SUBTYPE_MONITOR : (source->flags & PA_SOURCE_HARDWARE ? SUBTYPE_HARDWARE : SUBTYPE_VIRTUAL);
117
118     } else
119         pa_assert_not_reached();
120 }
121
122 static void txt_record_server_data(pa_core *c, TXTRecordRef *txt) {
123     char s[128];
124     char *t;
125
126     pa_assert(c);
127
128     TXTRecordSetValue(txt, "server-version", strlen(PACKAGE_NAME" "PACKAGE_VERSION), PACKAGE_NAME" "PACKAGE_VERSION);
129
130     t = pa_get_user_name_malloc();
131     TXTRecordSetValue(txt, "user-name", strlen(t), t);
132     pa_xfree(t);
133
134     t = pa_machine_id();
135     TXTRecordSetValue(txt, "machine-id", strlen(t), t);
136     pa_xfree(t);
137
138     t = pa_uname_string();
139     TXTRecordSetValue(txt, "uname", strlen(t), t);
140     pa_xfree(t);
141
142     t = pa_get_fqdn(s, sizeof(s));
143     TXTRecordSetValue(txt, "fqdn", strlen(t), t);
144
145     snprintf(s, sizeof(s), "0x%08x", c->cookie);
146     TXTRecordSetValue(txt, "cookie", strlen(s), s);
147 }
148
149 static void service_free(struct service *s);
150
151 static void dns_service_register_reply(DNSServiceRef sdRef,
152                                        DNSServiceFlags flags,
153                                        DNSServiceErrorType errorCode,
154                                        const char *name,
155                                        const char *regtype,
156                                        const char *domain,
157                                        void *context) {
158     struct service *s = context;
159
160     pa_assert(s);
161
162     switch (errorCode) {
163     case kDNSServiceErr_NameConflict:
164         pa_log("DNS service reported kDNSServiceErr_NameConflict\n");
165         service_free(s);
166         break;
167
168     case kDNSServiceErr_NoError:
169     default:
170         break;
171     }
172 }
173
174 static uint16_t compute_port(struct userdata *u) {
175     pa_strlist *i;
176
177     pa_assert(u);
178
179     for (i = pa_native_protocol_servers(u->native); i; i = pa_strlist_next(i)) {
180         pa_parsed_address a;
181
182         if (pa_parse_address(pa_strlist_data(i), &a) >= 0 &&
183             (a.type == PA_PARSED_ADDRESS_TCP4 ||
184              a.type == PA_PARSED_ADDRESS_TCP6 ||
185              a.type == PA_PARSED_ADDRESS_TCP_AUTO) &&
186             a.port > 0) {
187
188             pa_xfree(a.path_or_host);
189             return a.port;
190         }
191
192         pa_xfree(a.path_or_host);
193     }
194
195     return PA_NATIVE_DEFAULT_PORT;
196 }
197
198 static int publish_service(struct service *s) {
199     int r = -1;
200     TXTRecordRef txt;
201     DNSServiceErrorType err;
202     const char *name = NULL, *t;
203     pa_proplist *proplist = NULL;
204     pa_sample_spec ss;
205     pa_channel_map map;
206     char cm[PA_CHANNEL_MAP_SNPRINT_MAX], tmp[64];
207     enum service_subtype subtype;
208
209     const char * const subtype_text[] = {
210         [SUBTYPE_HARDWARE] = "hardware",
211         [SUBTYPE_VIRTUAL] = "virtual",
212         [SUBTYPE_MONITOR] = "monitor"
213     };
214
215     pa_assert(s);
216
217     if (s->service) {
218         DNSServiceRefDeallocate(s->service);
219         s->service = NULL;
220     }
221
222     TXTRecordCreate(&txt, 0, NULL);
223
224     txt_record_server_data(s->userdata->core, &txt);
225
226     get_service_data(s, &ss, &map, &name, &proplist, &subtype);
227     TXTRecordSetValue(&txt, "device", strlen(name), name);
228
229     snprintf(tmp, sizeof(tmp), "%u", ss.rate);
230     TXTRecordSetValue(&txt, "rate", strlen(tmp), tmp);
231
232     snprintf(tmp, sizeof(tmp), "%u", ss.channels);
233     TXTRecordSetValue(&txt, "channels", strlen(tmp), tmp);
234
235     t = pa_sample_format_to_string(ss.format);
236     TXTRecordSetValue(&txt, "format", strlen(t), t);
237
238     t = pa_channel_map_snprint(cm, sizeof(cm), &map);
239     TXTRecordSetValue(&txt, "channel_map", strlen(t), t);
240
241     t = subtype_text[subtype];
242     TXTRecordSetValue(&txt, "subtype", strlen(t), t);
243
244     if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_DESCRIPTION)))
245         TXTRecordSetValue(&txt, "description", strlen(t), t);
246     if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_ICON_NAME)))
247         TXTRecordSetValue(&txt, "icon-name", strlen(t), t);
248     if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_VENDOR_NAME)))
249         TXTRecordSetValue(&txt, "vendor-name", strlen(t), t);
250     if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_PRODUCT_NAME)))
251         TXTRecordSetValue(&txt, "product-name", strlen(t), t);
252     if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_CLASS)))
253         TXTRecordSetValue(&txt, "class", strlen(t), t);
254     if ((t = pa_proplist_gets(proplist, PA_PROP_DEVICE_FORM_FACTOR)))
255         TXTRecordSetValue(&txt, "form-factor", strlen(t), t);
256
257     err = DNSServiceRegister(&s->service,
258                              0,         /* flags */
259                              kDNSServiceInterfaceIndexAny,
260                              s->service_name,
261                              pa_sink_isinstance(s->device) ? SERVICE_TYPE_SINK : SERVICE_TYPE_SOURCE,
262                              NULL,      /* domain */
263                              NULL,      /* host */
264                              compute_port(s->userdata),
265                              TXTRecordGetLength(&txt),
266                              TXTRecordGetBytesPtr(&txt),
267                              dns_service_register_reply, s);
268
269     if (err != kDNSServiceErr_NoError) {
270         pa_log("DNSServiceRegister() returned err %d", err);
271         goto finish;
272     }
273
274     pa_log_debug("Successfully registered Bonjour services for >%s<.", s->service_name);
275     return 0;
276
277 finish:
278
279     /* Remove this service */
280     if (r < 0)
281         service_free(s);
282
283     TXTRecordDeallocate(&txt);
284
285     return r;
286 }
287
288 static struct service *get_service(struct userdata *u, pa_object *device) {
289     struct service *s;
290     char *hn, *un;
291     const char *n;
292
293     pa_assert(u);
294     pa_object_assert_ref(device);
295
296     if ((s = pa_hashmap_get(u->services, device)))
297         return s;
298
299     s = pa_xnew0(struct service, 1);
300     s->userdata = u;
301     s->device = device;
302
303     if (pa_sink_isinstance(device)) {
304         if (!(n = pa_proplist_gets(PA_SINK(device)->proplist, PA_PROP_DEVICE_DESCRIPTION)))
305             n = PA_SINK(device)->name;
306     } else {
307         if (!(n = pa_proplist_gets(PA_SOURCE(device)->proplist, PA_PROP_DEVICE_DESCRIPTION)))
308             n = PA_SOURCE(device)->name;
309     }
310
311     hn = pa_get_host_name_malloc();
312     un = pa_get_user_name_malloc();
313
314     s->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s: %s", un, hn, n), kDNSServiceMaxDomainName-1);
315
316     pa_xfree(un);
317     pa_xfree(hn);
318
319     pa_hashmap_put(u->services, s->device, s);
320
321     return s;
322 }
323
324 static void service_free(struct service *s) {
325     pa_assert(s);
326
327     pa_hashmap_remove(s->userdata->services, s->device);
328
329     if (s->service)
330         DNSServiceRefDeallocate(s->service);
331
332     pa_xfree(s->service_name);
333     pa_xfree(s);
334 }
335
336 static pa_bool_t shall_ignore(pa_object *o) {
337     pa_object_assert_ref(o);
338
339     if (pa_sink_isinstance(o))
340         return !!(PA_SINK(o)->flags & PA_SINK_NETWORK);
341
342     if (pa_source_isinstance(o))
343         return PA_SOURCE(o)->monitor_of || (PA_SOURCE(o)->flags & PA_SOURCE_NETWORK);
344
345     pa_assert_not_reached();
346 }
347
348 static pa_hook_result_t device_new_or_changed_cb(pa_core *c, pa_object *o, struct userdata *u) {
349     pa_assert(c);
350     pa_object_assert_ref(o);
351
352     if (!shall_ignore(o))
353         publish_service(get_service(u, o));
354
355     return PA_HOOK_OK;
356 }
357
358 static pa_hook_result_t device_unlink_cb(pa_core *c, pa_object *o, struct userdata *u) {
359     struct service *s;
360
361     pa_assert(c);
362     pa_object_assert_ref(o);
363
364     if ((s = pa_hashmap_get(u->services, o)))
365         service_free(s);
366
367     return PA_HOOK_OK;
368 }
369
370 static int publish_main_service(struct userdata *u) {
371     DNSServiceErrorType err;
372     TXTRecordRef txt;
373
374     pa_assert(u);
375
376     if (u->main_service) {
377         DNSServiceRefDeallocate(u->main_service);
378         u->main_service = NULL;
379     }
380
381     TXTRecordCreate(&txt, 0, NULL);
382     txt_record_server_data(u->core, &txt);
383
384     err = DNSServiceRegister(&u->main_service,
385                              0, /* flags */
386                              kDNSServiceInterfaceIndexAny,
387                              u->service_name,
388                              SERVICE_TYPE_SERVER,
389                              NULL, /* domain */
390                              NULL, /* host */
391                              compute_port(u),
392                              TXTRecordGetLength(&txt),
393                              TXTRecordGetBytesPtr(&txt),
394                              NULL, NULL);
395
396     if (err != kDNSServiceErr_NoError) {
397         pa_log("%s(): DNSServiceRegister() returned err %d", __func__, err);
398         return err;
399     }
400
401     TXTRecordDeallocate(&txt);
402
403     return 0;
404 }
405
406 static int publish_all_services(struct userdata *u) {
407     pa_sink *sink;
408     pa_source *source;
409     uint32_t idx;
410
411     pa_assert(u);
412
413     pa_log_debug("Publishing services in Bonjour");
414
415     for (sink = PA_SINK(pa_idxset_first(u->core->sinks, &idx)); sink; sink = PA_SINK(pa_idxset_next(u->core->sinks, &idx)))
416         if (!shall_ignore(PA_OBJECT(sink)))
417             publish_service(get_service(u, PA_OBJECT(sink)));
418
419     for (source = PA_SOURCE(pa_idxset_first(u->core->sources, &idx)); source; source = PA_SOURCE(pa_idxset_next(u->core->sources, &idx)))
420         if (!shall_ignore(PA_OBJECT(source)))
421             publish_service(get_service(u, PA_OBJECT(source)));
422
423     return publish_main_service(u);
424 }
425
426 static void unpublish_all_services(struct userdata *u) {
427     void *state = NULL;
428     struct service *s;
429
430     pa_assert(u);
431
432     pa_log_debug("Unpublishing services in Bonjour");
433
434     while ((s = pa_hashmap_iterate(u->services, &state, NULL)))
435         service_free(s);
436
437     if (u->main_service)
438         DNSServiceRefDeallocate(u->main_service);
439 }
440
441 int pa__init(pa_module*m) {
442
443     struct userdata *u;
444     pa_modargs *ma = NULL;
445     char *hn, *un;
446
447     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
448         pa_log("Failed to parse module arguments.");
449         goto fail;
450     }
451
452     m->userdata = u = pa_xnew0(struct userdata, 1);
453     u->core = m->core;
454     u->module = m;
455     u->native = pa_native_protocol_get(u->core);
456
457     u->services = pa_hashmap_new(pa_idxset_trivial_hash_func, pa_idxset_trivial_compare_func);
458
459     u->sink_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PUT], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
460     u->sink_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
461     u->sink_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SINK_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) device_unlink_cb, u);
462     u->source_new_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PUT], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
463     u->source_changed_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_PROPLIST_CHANGED], PA_HOOK_LATE, (pa_hook_cb_t) device_new_or_changed_cb, u);
464     u->source_unlink_slot = pa_hook_connect(&m->core->hooks[PA_CORE_HOOK_SOURCE_UNLINK], PA_HOOK_LATE, (pa_hook_cb_t) device_unlink_cb, u);
465
466     un = pa_get_user_name_malloc();
467     hn = pa_get_host_name_malloc();
468     u->service_name = pa_truncate_utf8(pa_sprintf_malloc("%s@%s", un, hn), kDNSServiceMaxDomainName-1);
469     pa_xfree(un);
470     pa_xfree(hn);
471
472     publish_all_services(u);
473     pa_modargs_free(ma);
474
475     return 0;
476
477 fail:
478     pa__done(m);
479
480     if (ma)
481         pa_modargs_free(ma);
482
483     return -1;
484 }
485
486 void pa__done(pa_module*m) {
487     struct userdata*u;
488     pa_assert(m);
489
490     if (!(u = m->userdata))
491         return;
492
493     unpublish_all_services(u);
494
495     if (u->services)
496         pa_hashmap_free(u->services, NULL, NULL);
497
498     if (u->sink_new_slot)
499         pa_hook_slot_free(u->sink_new_slot);
500     if (u->source_new_slot)
501         pa_hook_slot_free(u->source_new_slot);
502     if (u->sink_changed_slot)
503         pa_hook_slot_free(u->sink_changed_slot);
504     if (u->source_changed_slot)
505         pa_hook_slot_free(u->source_changed_slot);
506     if (u->sink_unlink_slot)
507         pa_hook_slot_free(u->sink_unlink_slot);
508     if (u->source_unlink_slot)
509         pa_hook_slot_free(u->source_unlink_slot);
510
511     if (u->native)
512         pa_native_protocol_unref(u->native);
513
514     pa_xfree(u->service_name);
515     pa_xfree(u);
516 }