1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2004-2006 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
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 published
9 by the Free Software Foundation; either version 2.1 of the License,
10 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 License
18 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <stdlib.h>
26 #include <stdio.h>
27 #include <signal.h>
28
29 #include <pulse/rtclock.h>
30 #include <pulse/timeval.h>
31 #include <pulse/xmalloc.h>
32
33 #include <pulsecore/module.h>
34 #include <pulsecore/core-rtclock.h>
35 #include <pulsecore/core-util.h>
36 #include <pulsecore/message-handler.h>
37 #include <pulsecore/core-scache.h>
38 #include <pulsecore/core-subscribe.h>
39 #include <pulsecore/random.h>
40 #include <pulsecore/log.h>
41 #include <pulsecore/macro.h>
42 #include <pulsecore/strbuf.h>
43
44 #include "log/audio_log.h"
45
46 #include "core.h"
47
48 PA_DEFINE_PUBLIC_CLASS(pa_core, pa_msgobject);
49
core_process_msg(pa_msgobject * o,int code,void * userdata,int64_t offset,pa_memchunk * chunk)50 static int core_process_msg(pa_msgobject *o, int code, void *userdata, int64_t offset, pa_memchunk *chunk) {
51 pa_core *c = PA_CORE(o);
52
53 pa_core_assert_ref(c);
54
55 switch (code) {
56
57 case PA_CORE_MESSAGE_UNLOAD_MODULE:
58 pa_module_unload(userdata, true);
59 return 0;
60
61 default:
62 return -1;
63 }
64 }
65
66 static void core_free(pa_object *o);
67
68 /* Returns a list of handlers. */
message_handler_list(pa_core * c)69 static char *message_handler_list(pa_core *c) {
70 pa_json_encoder *encoder;
71 void *state = NULL;
72 struct pa_message_handler *handler;
73
74 encoder = pa_json_encoder_new();
75
76 pa_json_encoder_begin_element_array(encoder);
77 PA_HASHMAP_FOREACH(handler, c->message_handlers, state) {
78 pa_json_encoder_begin_element_object(encoder);
79
80 pa_json_encoder_add_member_string(encoder, "name", handler->object_path);
81 pa_json_encoder_add_member_string(encoder, "description", handler->description);
82
83 pa_json_encoder_end_object(encoder);
84 }
85 pa_json_encoder_end_array(encoder);
86
87 return pa_json_encoder_to_string_free(encoder);
88 }
89
core_message_handler(const char * object_path,const char * message,const pa_json_object * parameters,char ** response,void * userdata)90 static int core_message_handler(const char *object_path, const char *message, const pa_json_object *parameters, char **response, void *userdata) {
91 pa_core *c = userdata;
92
93 pa_assert(c);
94 pa_assert(message);
95 pa_assert(response);
96 pa_assert(pa_safe_streq(object_path, "/core"));
97
98 if (pa_streq(message, "list-handlers")) {
99 *response = message_handler_list(c);
100 return PA_OK;
101 }
102
103 return -PA_ERR_NOTIMPLEMENTED;
104 }
105
pa_core_new(pa_mainloop_api * m,bool shared,bool enable_memfd,size_t shm_size)106 pa_core* pa_core_new(pa_mainloop_api *m, bool shared, bool enable_memfd, size_t shm_size) {
107 AUDIO_INFO_LOG("start pa_core_new, shared: %{public}d, enable_memfd: %{public}d", shared, enable_memfd);
108 pa_core* c;
109 pa_mempool *pool;
110 pa_mem_type_t type;
111 int j;
112
113 pa_assert(m);
114
115 if (shared) {
116 type = (enable_memfd) ? PA_MEM_TYPE_SHARED_MEMFD : PA_MEM_TYPE_SHARED_POSIX;
117 if (!(pool = pa_mempool_new(type, shm_size, false))) {
118 AUDIO_WARNING_LOG("Failed to allocate %{public}s memory pool. Falling back to a normal memory pool.",
119 pa_mem_type_to_string(type));
120 shared = false;
121 }
122 }
123
124 if (!shared) {
125 if (!(pool = pa_mempool_new(PA_MEM_TYPE_PRIVATE, shm_size, false))) {
126 AUDIO_ERR_LOG("pa_mempool_new() failed.");
127 return NULL;
128 }
129 }
130
131 c = pa_msgobject_new(pa_core);
132 c->parent.parent.free = core_free;
133 c->parent.process_msg = core_process_msg;
134
135 c->state = PA_CORE_STARTUP;
136 c->mainloop = m;
137
138 c->clients = pa_idxset_new(NULL, NULL);
139 c->cards = pa_idxset_new(NULL, NULL);
140 c->sinks = pa_idxset_new(NULL, NULL);
141 c->sources = pa_idxset_new(NULL, NULL);
142 c->sink_inputs = pa_idxset_new(NULL, NULL);
143 c->source_outputs = pa_idxset_new(NULL, NULL);
144 c->modules = pa_idxset_new(NULL, NULL);
145 c->scache = pa_idxset_new(NULL, NULL);
146
147 c->namereg = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
148 c->shared = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
149 c->message_handlers = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
150
151 pa_message_handler_register(c, "/core", "Core message handler", core_message_handler, (void *) c);
152
153 c->default_source = NULL;
154 c->default_sink = NULL;
155
156 c->default_sample_spec.format = PA_SAMPLE_S16NE;
157 c->default_sample_spec.rate = 44100;
158 c->default_sample_spec.channels = 2;
159 pa_channel_map_init_extend(&c->default_channel_map, c->default_sample_spec.channels, PA_CHANNEL_MAP_DEFAULT);
160 c->default_n_fragments = 4;
161 c->default_fragment_size_msec = 25;
162
163 c->deferred_volume_safety_margin_usec = 8000;
164 c->deferred_volume_extra_delay_usec = 0;
165
166 c->module_defer_unload_event = NULL;
167 c->modules_pending_unload = pa_hashmap_new(NULL, NULL);
168
169 c->subscription_defer_event = NULL;
170 PA_LLIST_HEAD_INIT(pa_subscription, c->subscriptions);
171 PA_LLIST_HEAD_INIT(pa_subscription_event, c->subscription_event_queue);
172 c->subscription_event_last = NULL;
173
174 c->mempool = pool;
175 c->shm_size = shm_size;
176 pa_silence_cache_init(&c->silence_cache);
177
178 c->exit_event = NULL;
179 c->scache_auto_unload_event = NULL;
180
181 c->exit_idle_time = -1;
182 c->scache_idle_time = 20;
183
184 c->flat_volumes = true;
185 c->disallow_module_loading = false;
186 c->disallow_exit = false;
187 c->running_as_daemon = false;
188 c->realtime_scheduling = false;
189 c->realtime_priority = 5;
190 c->disable_remixing = false;
191 c->remixing_use_all_sink_channels = true;
192 c->remixing_produce_lfe = false;
193 c->remixing_consume_lfe = false;
194 c->lfe_crossover_freq = 0;
195 c->deferred_volume = true;
196 c->resample_method = PA_RESAMPLER_SPEEX_FLOAT_BASE + 1;
197
198 for (j = 0; j < PA_CORE_HOOK_MAX; j++)
199 pa_hook_init(&c->hooks[j], c);
200
201 pa_random(&c->cookie, sizeof(c->cookie));
202
203 #ifdef SIGPIPE
204 pa_check_signal_is_blocked(SIGPIPE);
205 #endif
206
207 return c;
208 }
209
core_free(pa_object * o)210 static void core_free(pa_object *o) {
211 pa_core *c = PA_CORE(o);
212 int j;
213 pa_assert(c);
214
215 c->state = PA_CORE_SHUTDOWN;
216
217 /* Note: All modules and samples in the cache should be unloaded before
218 * we get here */
219
220 pa_assert(pa_idxset_isempty(c->scache));
221 pa_idxset_free(c->scache, NULL);
222
223 pa_assert(pa_idxset_isempty(c->modules));
224 pa_idxset_free(c->modules, NULL);
225
226 pa_assert(pa_idxset_isempty(c->clients));
227 pa_idxset_free(c->clients, NULL);
228
229 pa_assert(pa_idxset_isempty(c->cards));
230 pa_idxset_free(c->cards, NULL);
231
232 pa_assert(pa_idxset_isempty(c->sinks));
233 pa_idxset_free(c->sinks, NULL);
234
235 pa_assert(pa_idxset_isempty(c->sources));
236 pa_idxset_free(c->sources, NULL);
237
238 pa_assert(pa_idxset_isempty(c->source_outputs));
239 pa_idxset_free(c->source_outputs, NULL);
240
241 pa_assert(pa_idxset_isempty(c->sink_inputs));
242 pa_idxset_free(c->sink_inputs, NULL);
243
244 pa_assert(pa_hashmap_isempty(c->namereg));
245 pa_hashmap_free(c->namereg);
246
247 pa_assert(pa_hashmap_isempty(c->shared));
248 pa_hashmap_free(c->shared);
249
250 pa_message_handler_unregister(c, "/core");
251
252 pa_assert(pa_hashmap_isempty(c->message_handlers));
253 pa_hashmap_free(c->message_handlers);
254
255 pa_assert(pa_hashmap_isempty(c->modules_pending_unload));
256 pa_hashmap_free(c->modules_pending_unload);
257
258 pa_subscription_free_all(c);
259
260 if (c->exit_event)
261 c->mainloop->time_free(c->exit_event);
262
263 pa_assert(!c->default_source);
264 pa_assert(!c->default_sink);
265 pa_xfree(c->configured_default_source);
266 pa_xfree(c->configured_default_sink);
267
268 pa_silence_cache_done(&c->silence_cache);
269 pa_mempool_unref(c->mempool);
270
271 for (j = 0; j < PA_CORE_HOOK_MAX; j++)
272 pa_hook_done(&c->hooks[j]);
273
274 pa_xfree(c);
275 }
276
pa_core_set_configured_default_sink(pa_core * core,const char * sink)277 void pa_core_set_configured_default_sink(pa_core *core, const char *sink) {
278 char *old_sink;
279
280 pa_assert(core);
281
282 old_sink = pa_xstrdup(core->configured_default_sink);
283
284 if (pa_safe_streq(sink, old_sink))
285 goto finish;
286
287 pa_xfree(core->configured_default_sink);
288 core->configured_default_sink = pa_xstrdup(sink);
289 AUDIO_INFO_LOG("configured_default_sink: %{public}s -> %{public}s",
290 old_sink ? old_sink : "(unset)", sink ? sink : "(unset)");
291 pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SERVER | PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
292
293 pa_core_update_default_sink(core);
294
295 finish:
296 pa_xfree(old_sink);
297 }
298
pa_core_set_configured_default_source(pa_core * core,const char * source)299 void pa_core_set_configured_default_source(pa_core *core, const char *source) {
300 char *old_source;
301
302 pa_assert(core);
303
304 old_source = pa_xstrdup(core->configured_default_source);
305
306 if (pa_safe_streq(source, old_source))
307 goto finish;
308
309 pa_xfree(core->configured_default_source);
310 core->configured_default_source = pa_xstrdup(source);
311 AUDIO_INFO_LOG("configured_default_source: %{public}s -> %{public}s",
312 old_source ? old_source : "(unset)", source ? source : "(unset)");
313 pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SERVER | PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
314
315 pa_core_update_default_source(core);
316
317 finish:
318 pa_xfree(old_source);
319 }
320
321 /* a < b -> return -1
322 * a == b -> return 0
323 * a > b -> return 1 */
compare_sinks(pa_sink * a,pa_sink * b)324 static int compare_sinks(pa_sink *a, pa_sink *b) {
325 pa_core *core;
326
327 core = a->core;
328
329 /* Available sinks always beat unavailable sinks. */
330 if (a->active_port && a->active_port->available == PA_AVAILABLE_NO
331 && (!b->active_port || b->active_port->available != PA_AVAILABLE_NO))
332 return -1;
333 if (b->active_port && b->active_port->available == PA_AVAILABLE_NO
334 && (!a->active_port || a->active_port->available != PA_AVAILABLE_NO))
335 return 1;
336
337 /* The configured default sink is preferred over any other sink. */
338 if (pa_safe_streq(b->name, core->configured_default_sink))
339 return -1;
340 if (pa_safe_streq(a->name, core->configured_default_sink))
341 return 1;
342
343 if (a->priority < b->priority)
344 return -1;
345 if (a->priority > b->priority)
346 return 1;
347
348 /* It's hard to find any difference between these sinks, but maybe one of
349 * them is already the default sink? If so, it's best to keep it as the
350 * default to avoid changing the routing for no good reason. */
351 if (b == core->default_sink)
352 return -1;
353 if (a == core->default_sink)
354 return 1;
355
356 return 0;
357 }
358
pa_core_update_default_sink(pa_core * core)359 void pa_core_update_default_sink(pa_core *core) {
360 pa_sink *best = NULL;
361 pa_sink *sink;
362 uint32_t idx;
363 pa_sink *old_default_sink;
364
365 pa_assert(core);
366
367 PA_IDXSET_FOREACH(sink, core->sinks, idx) {
368 if (!PA_SINK_IS_LINKED(sink->state))
369 continue;
370
371 if (!best) {
372 best = sink;
373 continue;
374 }
375
376 if (compare_sinks(sink, best) > 0)
377 best = sink;
378 }
379
380 old_default_sink = core->default_sink;
381
382 if (best == old_default_sink)
383 return;
384
385 core->default_sink = best;
386 AUDIO_INFO_LOG("default_sink: %{public}s -> %{public}s",
387 old_default_sink ? old_default_sink->name : "(unset)", best ? best->name : "(unset)");
388
389 /* If the default sink changed, it may be that the default source has to be
390 * changed too, because monitor sources are prioritized partly based on the
391 * priorities of the monitored sinks. */
392 pa_core_update_default_source(core);
393
394 pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SERVER | PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
395 pa_hook_fire(&core->hooks[PA_CORE_HOOK_DEFAULT_SINK_CHANGED], core->default_sink);
396
397 /* try to move the streams from old_default_sink to the new default_sink conditionally */
398 if (old_default_sink)
399 pa_sink_move_streams_to_default_sink(core, old_default_sink, true);
400 }
401
402 /* a < b -> return -1
403 * a == b -> return 0
404 * a > b -> return 1 */
compare_sources(pa_source * a,pa_source * b)405 static int compare_sources(pa_source *a, pa_source *b) {
406 pa_core *core;
407
408 core = a->core;
409
410 /* Available sources always beat unavailable sources. */
411 if (a->active_port && a->active_port->available == PA_AVAILABLE_NO
412 && (!b->active_port || b->active_port->available != PA_AVAILABLE_NO))
413 return -1;
414 if (b->active_port && b->active_port->available == PA_AVAILABLE_NO
415 && (!a->active_port || a->active_port->available != PA_AVAILABLE_NO))
416 return 1;
417
418 /* The configured default source is preferred over any other source. */
419 if (pa_safe_streq(b->name, core->configured_default_source))
420 return -1;
421 if (pa_safe_streq(a->name, core->configured_default_source))
422 return 1;
423
424 /* Monitor sources lose to non-monitor sources. */
425 if (a->monitor_of && !b->monitor_of)
426 return -1;
427 if (!a->monitor_of && b->monitor_of)
428 return 1;
429
430 if (a->priority < b->priority)
431 return -1;
432 if (a->priority > b->priority)
433 return 1;
434
435 /* If the sources are monitors, we can compare the monitored sinks. */
436 if (a->monitor_of)
437 return compare_sinks(a->monitor_of, b->monitor_of);
438
439 /* It's hard to find any difference between these sources, but maybe one of
440 * them is already the default source? If so, it's best to keep it as the
441 * default to avoid changing the routing for no good reason. */
442 if (b == core->default_source)
443 return -1;
444 if (a == core->default_source)
445 return 1;
446
447 return 0;
448 }
449
pa_core_update_default_source(pa_core * core)450 void pa_core_update_default_source(pa_core *core) {
451 pa_source *best = NULL;
452 pa_source *source;
453 uint32_t idx;
454 pa_source *old_default_source;
455
456 pa_assert(core);
457
458 PA_IDXSET_FOREACH(source, core->sources, idx) {
459 if (!PA_SOURCE_IS_LINKED(source->state))
460 continue;
461
462 if (!best) {
463 best = source;
464 continue;
465 }
466
467 if (compare_sources(source, best) > 0)
468 best = source;
469 }
470
471 old_default_source = core->default_source;
472
473 if (best == old_default_source)
474 return;
475
476 core->default_source = best;
477 AUDIO_INFO_LOG("default_source: %{public}s -> %{public}s",
478 old_default_source ? old_default_source->name : "(unset)", best ? best->name : "(unset)");
479 pa_subscription_post(core, PA_SUBSCRIPTION_EVENT_SERVER | PA_SUBSCRIPTION_EVENT_CHANGE, PA_INVALID_INDEX);
480 pa_hook_fire(&core->hooks[PA_CORE_HOOK_DEFAULT_SOURCE_CHANGED], core->default_source);
481
482 /* try to move the streams from old_default_source to the new default_source conditionally */
483 if (old_default_source)
484 pa_source_move_streams_to_default_source(core, old_default_source, true);
485 }
486
pa_core_set_exit_idle_time(pa_core * core,int time)487 void pa_core_set_exit_idle_time(pa_core *core, int time) {
488 pa_assert(core);
489
490 if (time == core->exit_idle_time)
491 return;
492
493 AUDIO_INFO_LOG("exit_idle_time: %{public}i -> %{public}i", core->exit_idle_time, time);
494 core->exit_idle_time = time;
495 }
496
exit_callback(pa_mainloop_api * m,pa_time_event * e,const struct timeval * t,void * userdata)497 static void exit_callback(pa_mainloop_api *m, pa_time_event *e, const struct timeval *t, void *userdata) {
498 pa_core *c = userdata;
499 pa_assert(c->exit_event == e);
500
501 AUDIO_INFO_LOG("We are idle, quitting...");
502 pa_core_exit(c, true, 0);
503 }
504
pa_core_check_idle(pa_core * c)505 void pa_core_check_idle(pa_core *c) {
506 pa_assert(c);
507
508 if (!c->exit_event &&
509 c->exit_idle_time >= 0 &&
510 pa_idxset_size(c->clients) == 0) {
511
512 c->exit_event = pa_core_rttime_new(c, pa_rtclock_now() + c->exit_idle_time * PA_USEC_PER_SEC, exit_callback, c);
513
514 } else if (c->exit_event && pa_idxset_size(c->clients) > 0) {
515 c->mainloop->time_free(c->exit_event);
516 c->exit_event = NULL;
517 }
518 }
519
pa_core_exit(pa_core * c,bool force,int retval)520 int pa_core_exit(pa_core *c, bool force, int retval) {
521 pa_assert(c);
522
523 if (c->disallow_exit && !force)
524 return -1;
525
526 c->mainloop->quit(c->mainloop, retval);
527 return 0;
528 }
529
pa_core_maybe_vacuum(pa_core * c)530 void pa_core_maybe_vacuum(pa_core *c) {
531 pa_assert(c);
532
533 if (pa_idxset_isempty(c->sink_inputs) && pa_idxset_isempty(c->source_outputs)) {
534 AUDIO_DEBUG_LOG("Hmm, no streams around, trying to vacuum.");
535 } else {
536 pa_sink *si;
537 pa_source *so;
538 uint32_t idx;
539
540 idx = 0;
541 PA_IDXSET_FOREACH(si, c->sinks, idx)
542 if (si->state != PA_SINK_SUSPENDED)
543 return;
544
545 idx = 0;
546 PA_IDXSET_FOREACH(so, c->sources, idx)
547 if (so->state != PA_SOURCE_SUSPENDED)
548 return;
549
550 AUDIO_INFO_LOG("All sinks and sources are suspended, vacuuming memory");
551 }
552
553 pa_mempool_vacuum(c->mempool);
554 }
555
pa_core_rttime_new(pa_core * c,pa_usec_t usec,pa_time_event_cb_t cb,void * userdata)556 pa_time_event* pa_core_rttime_new(pa_core *c, pa_usec_t usec, pa_time_event_cb_t cb, void *userdata) {
557 struct timeval tv;
558
559 pa_assert(c);
560 pa_assert(c->mainloop);
561
562 return c->mainloop->time_new(c->mainloop, pa_timeval_rtstore(&tv, usec, true), cb, userdata);
563 }
564
pa_core_rttime_restart(pa_core * c,pa_time_event * e,pa_usec_t usec)565 void pa_core_rttime_restart(pa_core *c, pa_time_event *e, pa_usec_t usec) {
566 struct timeval tv;
567
568 pa_assert(c);
569 pa_assert(c->mainloop);
570
571 c->mainloop->time_restart(e, pa_timeval_rtstore(&tv, usec, true));
572 }
573
pa_core_move_streams_to_newly_available_preferred_sink(pa_core * c,pa_sink * s)574 void pa_core_move_streams_to_newly_available_preferred_sink(pa_core *c, pa_sink *s) {
575 pa_sink_input *si;
576 uint32_t idx;
577
578 pa_assert(c);
579 pa_assert(s);
580
581 PA_IDXSET_FOREACH(si, c->sink_inputs, idx) {
582 if (si->sink == s)
583 continue;
584
585 if (!si->sink)
586 continue;
587
588 /* Skip this sink input if it is connecting a filter sink to
589 * the master */
590 if (si->origin_sink)
591 continue;
592
593 /* It might happen that a stream and a sink are set up at the
594 same time, in which case we want to make sure we don't
595 interfere with that */
596 if (!PA_SINK_INPUT_IS_LINKED(si->state))
597 continue;
598
599 if (pa_safe_streq(si->preferred_sink, s->name))
600 pa_sink_input_move_to(si, s, false);
601 }
602
603 }
604
pa_core_move_streams_to_newly_available_preferred_source(pa_core * c,pa_source * s)605 void pa_core_move_streams_to_newly_available_preferred_source(pa_core *c, pa_source *s) {
606 pa_source_output *so;
607 uint32_t idx;
608
609 pa_assert(c);
610 pa_assert(s);
611
612 PA_IDXSET_FOREACH(so, c->source_outputs, idx) {
613 if (so->source == s)
614 continue;
615
616 if (so->direct_on_input)
617 continue;
618
619 if (!so->source)
620 continue;
621
622 /* Skip this source output if it is connecting a filter source to
623 * the master */
624 if (so->destination_source)
625 continue;
626
627 /* It might happen that a stream and a source are set up at the
628 same time, in which case we want to make sure we don't
629 interfere with that */
630 if (!PA_SOURCE_OUTPUT_IS_LINKED(so->state))
631 continue;
632
633 if (pa_safe_streq(so->preferred_source, s->name))
634 pa_source_output_move_to(so, s, false);
635 }
636
637 }
638
639
640 /* Helper macro to reduce repetition in pa_suspend_cause_to_string().
641 * Parameters:
642 * char *p: the current position in the write buffer
643 * bool first: is cause_to_check the first cause to be written?
644 * pa_suspend_cause_t cause_bitfield: the causes given to pa_suspend_cause_to_string()
645 * pa_suspend_cause_t cause_to_check: the cause whose presence in cause_bitfield is to be checked
646 */
647 #define CHECK_CAUSE(p, first, cause_bitfield, cause_to_check) \
648 if (cause_bitfield & PA_SUSPEND_##cause_to_check) { \
649 size_t len = sizeof(#cause_to_check) - 1; \
650 if (!first) { \
651 *p = '|'; \
652 p++; \
653 } \
654 first = false; \
655 memcpy(p, #cause_to_check, len); \
656 p += len; \
657 }
658
pa_suspend_cause_to_string(pa_suspend_cause_t cause_bitfield,char buf[PA_SUSPEND_CAUSE_TO_STRING_BUF_SIZE])659 const char *pa_suspend_cause_to_string(pa_suspend_cause_t cause_bitfield, char buf[PA_SUSPEND_CAUSE_TO_STRING_BUF_SIZE]) {
660 char *p = buf;
661 bool first = true;
662
663 CHECK_CAUSE(p, first, cause_bitfield, USER);
664 CHECK_CAUSE(p, first, cause_bitfield, APPLICATION);
665 CHECK_CAUSE(p, first, cause_bitfield, IDLE);
666 CHECK_CAUSE(p, first, cause_bitfield, SESSION);
667 CHECK_CAUSE(p, first, cause_bitfield, PASSTHROUGH);
668 CHECK_CAUSE(p, first, cause_bitfield, INTERNAL);
669 CHECK_CAUSE(p, first, cause_bitfield, UNAVAILABLE);
670
671 if (p == buf) {
672 memcpy(p, "(none)", 6);
673 p += 6;
674 }
675
676 *p = 0;
677
678 return buf;
679 }
680