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