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