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