1 /* PipeWire
2  *
3  * Copyright © 2018 Wim Taymans
4  *
5  * Permission is hereby granted, free of charge, to any person obtaining a
6  * copy of this software and associated documentation files (the "Software"),
7  * to deal in the Software without restriction, including without limitation
8  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9  * and/or sell copies of the Software, and to permit persons to whom the
10  * Software is furnished to do so, subject to the following conditions:
11  *
12  * The above copyright notice and this permission notice (including the next
13  * paragraph) shall be included in all copies or substantial portions of the
14  * Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22  * DEALINGS IN THE SOFTWARE.
23  */
24 #include <errno.h>
25 #include <unistd.h>
26 #include <time.h>
27 #include <stdio.h>
28 #include <regex.h>
29 #include <limits.h>
30 #include <sys/mman.h>
31 
32 #include <pipewire/log.h>
33 
34 #include <spa/support/cpu.h>
35 #include <spa/support/dbus.h>
36 #include <spa/support/plugin.h>
37 #include <spa/support/plugin-loader.h>
38 #include <spa/node/utils.h>
39 #include <spa/utils/names.h>
40 #include <spa/utils/string.h>
41 #include <spa/debug/format.h>
42 #include <spa/debug/types.h>
43 
44 #include <pipewire/impl.h>
45 #include <pipewire/private.h>
46 #include <pipewire/thread.h>
47 #include <pipewire/conf.h>
48 
49 #include <pipewire/extensions/protocol-native.h>
50 
51 PW_LOG_TOPIC_EXTERN(log_context);
52 #define PW_LOG_TOPIC_DEFAULT log_context
53 
54 /** \cond */
55 struct impl {
56 	struct pw_context this;
57 	struct spa_handle *dbus_handle;
58 	struct spa_plugin_loader plugin_loader;
59 	unsigned int recalc:1;
60 	unsigned int recalc_pending:1;
61 };
62 
63 
64 struct factory_entry {
65 	regex_t regex;
66 	char *lib;
67 };
68 /** \endcond */
69 
fill_properties(struct pw_context * context)70 static void fill_properties(struct pw_context *context)
71 {
72 	struct pw_properties *properties = context->properties;
73 
74 	if (!pw_properties_get(properties, PW_KEY_APP_NAME))
75 		pw_properties_set(properties, PW_KEY_APP_NAME, pw_get_client_name());
76 
77 	if (!pw_properties_get(properties, PW_KEY_APP_PROCESS_BINARY))
78 		pw_properties_set(properties, PW_KEY_APP_PROCESS_BINARY, pw_get_prgname());
79 
80 	if (!pw_properties_get(properties, PW_KEY_APP_LANGUAGE)) {
81 		pw_properties_set(properties, PW_KEY_APP_LANGUAGE, getenv("LANG"));
82 	}
83 	if (!pw_properties_get(properties, PW_KEY_APP_PROCESS_ID)) {
84 		pw_properties_setf(properties, PW_KEY_APP_PROCESS_ID, "%zd", (size_t) getpid());
85 	}
86 	if (!pw_properties_get(properties, PW_KEY_APP_PROCESS_USER))
87 		pw_properties_set(properties, PW_KEY_APP_PROCESS_USER, pw_get_user_name());
88 
89 	if (!pw_properties_get(properties, PW_KEY_APP_PROCESS_HOST))
90 		pw_properties_set(properties, PW_KEY_APP_PROCESS_HOST, pw_get_host_name());
91 
92 	if (!pw_properties_get(properties, PW_KEY_APP_PROCESS_SESSION_ID)) {
93 		pw_properties_set(properties, PW_KEY_APP_PROCESS_SESSION_ID,
94 				  getenv("XDG_SESSION_ID"));
95 	}
96 	if (!pw_properties_get(properties, PW_KEY_WINDOW_X11_DISPLAY)) {
97 		pw_properties_set(properties, PW_KEY_WINDOW_X11_DISPLAY,
98 				  getenv("DISPLAY"));
99 	}
100 	pw_properties_set(properties, PW_KEY_CORE_VERSION, context->core->info.version);
101 	pw_properties_set(properties, PW_KEY_CORE_NAME, context->core->info.name);
102 }
103 
try_load_conf(struct pw_context * this,const char * conf_prefix,const char * conf_name,struct pw_properties * conf)104 static int try_load_conf(struct pw_context *this, const char *conf_prefix,
105 		const char *conf_name, struct pw_properties *conf)
106 {
107 	int res;
108 
109 	if (conf_name == NULL)
110 		return -EINVAL;
111 	if (spa_streq(conf_name, "null"))
112 		return 0;
113 	if ((res = pw_conf_load_conf(conf_prefix, conf_name, conf)) < 0) {
114 		bool skip_prefix = conf_prefix == NULL || conf_name[0] == '/';
115 		pw_log_warn("%p: can't load config %s%s%s: %s",
116 				this,
117 				skip_prefix ? "" : conf_prefix,
118 				skip_prefix ? "" : "/",
119 				conf_name, spa_strerror(res));
120 	}
121 	return res;
122 }
123 
context_set_freewheel(struct pw_context * context,bool freewheel)124 static int context_set_freewheel(struct pw_context *context, bool freewheel)
125 {
126 	struct spa_thread *thr;
127 	int res;
128 
129 	if ((thr = pw_data_loop_get_thread(context->data_loop_impl)) == NULL)
130 		return -EIO;
131 
132 	if (freewheel) {
133 		pw_log_info("%p: enter freewheel", context);
134 		res = pw_thread_utils_drop_rt(thr);
135 	} else {
136 		pw_log_info("%p: exit freewheel", context);
137 		res = pw_thread_utils_acquire_rt(thr, 88);
138 	}
139 	if (res < 0)
140 		pw_log_info("%p: freewheel error:%s", context, spa_strerror(res));
141 
142 	context->freewheeling = freewheel;
143 
144 	return res;
145 }
146 
impl_plugin_loader_load(void * object,const char * factory_name,const struct spa_dict * info)147 static struct spa_handle *impl_plugin_loader_load(void *object, const char *factory_name, const struct spa_dict *info)
148 {
149 	struct impl *impl = object;
150 
151 	if (impl == NULL || factory_name == NULL) {
152 		errno = EINVAL;
153 		return NULL;
154 	}
155 
156 	return pw_context_load_spa_handle(&impl->this, factory_name, info);
157 }
158 
impl_plugin_loader_unload(void * object,struct spa_handle * handle)159 static int impl_plugin_loader_unload(void *object, struct spa_handle *handle)
160 {
161 	spa_return_val_if_fail(object != NULL, -EINVAL);
162 	return pw_unload_spa_handle(handle);
163 }
164 
165 static const struct spa_plugin_loader_methods impl_plugin_loader = {
166         SPA_VERSION_PLUGIN_LOADER_METHODS,
167         .load = impl_plugin_loader_load,
168 	.unload = impl_plugin_loader_unload,
169 };
170 
init_plugin_loader(struct impl * impl)171 static void init_plugin_loader(struct impl *impl)
172 {
173 	impl->plugin_loader.iface = SPA_INTERFACE_INIT(
174 		SPA_TYPE_INTERFACE_PluginLoader,
175 		SPA_VERSION_PLUGIN_LOADER,
176 		&impl_plugin_loader,
177 		impl);
178 }
179 
180 
181 /** Create a new context object
182  *
183  * \param main_loop the main loop to use
184  * \param properties extra properties for the context, ownership it taken
185  *
186  * \return a newly allocated context object
187  */
188 SPA_EXPORT
pw_context_new(struct pw_loop * main_loop,struct pw_properties * properties,size_t user_data_size)189 struct pw_context *pw_context_new(struct pw_loop *main_loop,
190 			    struct pw_properties *properties,
191 			    size_t user_data_size)
192 {
193 	struct impl *impl;
194 	struct pw_context *this;
195 	const char *lib, *str, *conf_prefix, *conf_name;
196 	void *dbus_iface = NULL;
197 	uint32_t n_support;
198 	struct pw_properties *pr, *conf;
199 	struct spa_cpu *cpu;
200 	int res = 0;
201 
202 	impl = calloc(1, sizeof(struct impl) + user_data_size);
203 	if (impl == NULL) {
204 		res = -errno;
205 		goto error_cleanup;
206 	}
207 
208 	this = &impl->this;
209 
210 	pw_log_debug("%p: new", this);
211 
212 	if (user_data_size > 0)
213 		this->user_data = SPA_PTROFF(impl, sizeof(struct impl), void);
214 
215 	pw_array_init(&this->factory_lib, 32);
216 	pw_array_init(&this->objects, 32);
217 	pw_map_init(&this->globals, 128, 32);
218 
219 	spa_list_init(&this->core_impl_list);
220 	spa_list_init(&this->protocol_list);
221 	spa_list_init(&this->core_list);
222 	spa_list_init(&this->registry_resource_list);
223 	spa_list_init(&this->global_list);
224 	spa_list_init(&this->module_list);
225 	spa_list_init(&this->device_list);
226 	spa_list_init(&this->client_list);
227 	spa_list_init(&this->node_list);
228 	spa_list_init(&this->factory_list);
229 	spa_list_init(&this->metadata_list);
230 	spa_list_init(&this->link_list);
231 	spa_list_init(&this->control_list[0]);
232 	spa_list_init(&this->control_list[1]);
233 	spa_list_init(&this->export_list);
234 	spa_list_init(&this->driver_list);
235 	spa_hook_list_init(&this->listener_list);
236 	spa_hook_list_init(&this->driver_listener_list);
237 
238 	this->sc_pagesize = sysconf(_SC_PAGESIZE);
239 
240 	if (properties == NULL)
241 		properties = pw_properties_new(NULL, NULL);
242 	if (properties == NULL) {
243 		res = -errno;
244 		goto error_free;
245 	}
246 	this->properties = properties;
247 
248 	conf = pw_properties_new(NULL, NULL);
249 	if (conf == NULL) {
250 		res = -errno;
251 		goto error_free;
252 	}
253 	this->conf = conf;
254 
255 	conf_prefix = getenv("PIPEWIRE_CONFIG_PREFIX");
256 	if (conf_prefix == NULL)
257 		conf_prefix = pw_properties_get(properties, PW_KEY_CONFIG_PREFIX);
258 
259 	conf_name = getenv("PIPEWIRE_CONFIG_NAME");
260 	if (try_load_conf(this, conf_prefix, conf_name, conf) < 0) {
261 		conf_name = pw_properties_get(properties, PW_KEY_CONFIG_NAME);
262 		if (try_load_conf(this, conf_prefix, conf_name, conf) < 0) {
263 			conf_name = "client.conf";
264 			if ((res = try_load_conf(this, conf_prefix, conf_name, conf)) < 0) {
265 				pw_log_error("%p: can't load config %s: %s",
266 					this, conf_name, spa_strerror(res));
267 				goto error_free;
268 			}
269 		}
270 	}
271 
272 	n_support = pw_get_support(this->support, SPA_N_ELEMENTS(this->support) - 6);
273 	cpu = spa_support_find(this->support, n_support, SPA_TYPE_INTERFACE_CPU);
274 
275 	if ((str = pw_properties_get(conf, "context.properties")) != NULL) {
276 		pw_properties_update_string(properties, str, strlen(str));
277 		pw_log_info("%p: parsed context.properties section", this);
278 	}
279 
280 	if ((str = getenv("PIPEWIRE_CORE"))) {
281 		pw_log_info("using core.name from environment: %s", str);
282 		pw_properties_set(properties, PW_KEY_CORE_NAME, str);
283 	}
284 
285 	if ((str = pw_properties_get(properties, "vm.overrides")) != NULL) {
286 		if (cpu != NULL && spa_cpu_get_vm_type(cpu) != SPA_CPU_VM_NONE)
287 			pw_properties_update_string(properties, str, strlen(str));
288 		pw_properties_set(properties, "vm.overrides", NULL);
289 	}
290 	if (cpu != NULL) {
291 		if (pw_properties_get(properties, PW_KEY_CPU_MAX_ALIGN) == NULL)
292 			pw_properties_setf(properties, PW_KEY_CPU_MAX_ALIGN,
293 				"%u", spa_cpu_get_max_align(cpu));
294 		if ((str = pw_properties_get(properties, SPA_KEY_CPU_ZERO_DENORMALS)) == NULL)
295 			str = "true";
296 		spa_cpu_zero_denormals(cpu, spa_atob(str));
297 	}
298 
299 	if (getenv("PIPEWIRE_DEBUG") == NULL &&
300 	    (str = pw_properties_get(properties, "log.level")) != NULL)
301 		pw_log_set_level(atoi(str));
302 
303 	if (pw_properties_get_bool(properties, "mem.mlock-all", false)) {
304 		if (mlockall(MCL_CURRENT | MCL_FUTURE) < 0)
305 			pw_log_warn("%p: could not mlockall; %m", impl);
306 		else
307 			pw_log_info("%p: mlockall succeeded", impl);
308 	}
309 
310 	pw_settings_init(this);
311 	this->settings = this->defaults;
312 
313 	pr = pw_properties_copy(properties);
314 	if ((str = pw_properties_get(pr, "context.data-loop." PW_KEY_LIBRARY_NAME_SYSTEM)))
315 		pw_properties_set(pr, PW_KEY_LIBRARY_NAME_SYSTEM, str);
316 
317 	this->data_loop_impl = pw_data_loop_new(&pr->dict);
318 	pw_properties_free(pr);
319 	if (this->data_loop_impl == NULL)  {
320 		res = -errno;
321 		goto error_free;
322 	}
323 
324 	this->pool = pw_mempool_new(NULL);
325 	if (this->pool == NULL) {
326 		res = -errno;
327 		goto error_free;
328 	}
329 
330 	this->data_loop = pw_data_loop_get_loop(this->data_loop_impl);
331 	this->data_system = this->data_loop->system;
332 	this->main_loop = main_loop;
333 
334 	init_plugin_loader(impl);
335 
336 	this->support[n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_System, this->main_loop->system);
337 	this->support[n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_Loop, this->main_loop->loop);
338 	this->support[n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_LoopUtils, this->main_loop->utils);
339 	this->support[n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_DataSystem, this->data_system);
340 	this->support[n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_DataLoop, this->data_loop->loop);
341 	this->support[n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_PluginLoader, &impl->plugin_loader);
342 
343 	if ((str = pw_properties_get(properties, "support.dbus")) == NULL ||
344 	    pw_properties_parse_bool(str)) {
345 		lib = pw_properties_get(properties, PW_KEY_LIBRARY_NAME_DBUS);
346 		if (lib == NULL)
347 			lib = "support/libspa-dbus";
348 
349 		impl->dbus_handle = pw_load_spa_handle(lib,
350 				SPA_NAME_SUPPORT_DBUS, NULL,
351 				n_support, this->support);
352 
353 		if (impl->dbus_handle == NULL) {
354 			pw_log_warn("%p: can't load dbus library: %s", this, lib);
355 		} else if ((res = spa_handle_get_interface(impl->dbus_handle,
356 							   SPA_TYPE_INTERFACE_DBus, &dbus_iface)) < 0) {
357 			pw_log_warn("%p: can't load dbus interface: %s", this, spa_strerror(res));
358 		} else {
359 			this->support[n_support++] = SPA_SUPPORT_INIT(SPA_TYPE_INTERFACE_DBus, dbus_iface);
360 		}
361 	}
362 	this->n_support = n_support;
363 	spa_assert(n_support <= SPA_N_ELEMENTS(this->support));
364 
365 	this->core = pw_context_create_core(this, pw_properties_copy(properties), 0);
366 	if (this->core == NULL) {
367 		res = -errno;
368 		goto error_free;
369 	}
370 	pw_impl_core_register(this->core, NULL);
371 
372 	fill_properties(this);
373 
374 	if ((res = pw_context_parse_conf_section(this, conf, "context.spa-libs")) < 0)
375 		goto error_free;
376 	pw_log_info("%p: parsed %d context.spa-libs items", this, res);
377 	if ((res = pw_context_parse_conf_section(this, conf, "context.modules")) < 0)
378 		goto error_free;
379 	if (res > 0)
380 		pw_log_info("%p: parsed %d context.modules items", this, res);
381 	else
382 		pw_log_warn("%p: no modules loaded from context.modules", this);
383 	if ((res = pw_context_parse_conf_section(this, conf, "context.objects")) < 0)
384 		goto error_free;
385 	pw_log_info("%p: parsed %d context.objects items", this, res);
386 	if ((res = pw_context_parse_conf_section(this, conf, "context.exec")) < 0)
387 		goto error_free;
388 	pw_log_info("%p: parsed %d context.exec items", this, res);
389 
390 	if ((res = pw_data_loop_start(this->data_loop_impl)) < 0)
391 		goto error_free;
392 
393 	context_set_freewheel(this, false);
394 
395 	pw_settings_expose(this);
396 
397 	pw_log_debug("%p: created", this);
398 
399 	return this;
400 
401 error_free:
402 	pw_context_destroy(this);
403 error_cleanup:
404 	errno = -res;
405 	return NULL;
406 }
407 
408 /** Destroy a context object
409  *
410  * \param context a context to destroy
411  */
412 SPA_EXPORT
pw_context_destroy(struct pw_context * context)413 void pw_context_destroy(struct pw_context *context)
414 {
415 	struct impl *impl = SPA_CONTAINER_OF(context, struct impl, this);
416 	struct pw_global *global;
417 	struct pw_impl_client *client;
418 	struct pw_impl_module *module;
419 	struct pw_impl_device *device;
420 	struct pw_core *core;
421 	struct pw_resource *resource;
422 	struct pw_impl_node *node;
423 	struct factory_entry *entry;
424 	struct pw_impl_metadata *metadata;
425 	struct pw_impl_core *core_impl;
426 
427 	pw_log_debug("%p: destroy", context);
428 	pw_context_emit_destroy(context);
429 
430 	spa_list_consume(core, &context->core_list, link)
431 		pw_core_disconnect(core);
432 
433 	spa_list_consume(client, &context->client_list, link)
434 		pw_impl_client_destroy(client);
435 
436 	spa_list_consume(node, &context->node_list, link)
437 		pw_impl_node_destroy(node);
438 
439 	spa_list_consume(device, &context->device_list, link)
440 		pw_impl_device_destroy(device);
441 
442 	spa_list_consume(resource, &context->registry_resource_list, link)
443 		pw_resource_destroy(resource);
444 
445 	if (context->data_loop_impl)
446 		pw_data_loop_destroy(context->data_loop_impl);
447 
448 	spa_list_consume(module, &context->module_list, link)
449 		pw_impl_module_destroy(module);
450 
451 	spa_list_consume(global, &context->global_list, link)
452 		pw_global_destroy(global);
453 
454 	spa_list_consume(metadata, &context->metadata_list, link)
455 		pw_impl_metadata_destroy(metadata);
456 
457 	spa_list_consume(core_impl, &context->core_impl_list, link)
458 		pw_impl_core_destroy(core_impl);
459 
460 	pw_log_debug("%p: free", context);
461 	pw_context_emit_free(context);
462 
463 	if (context->pool)
464 		pw_mempool_destroy(context->pool);
465 
466 	if (context->work_queue)
467 		pw_work_queue_destroy(context->work_queue);
468 
469 	pw_properties_free(context->properties);
470 	pw_properties_free(context->conf);
471 
472 	pw_settings_clean(context);
473 
474 	if (impl->dbus_handle)
475 		pw_unload_spa_handle(impl->dbus_handle);
476 
477 	pw_array_for_each(entry, &context->factory_lib) {
478 		regfree(&entry->regex);
479 		free(entry->lib);
480 	}
481 	pw_array_clear(&context->factory_lib);
482 
483 	pw_array_clear(&context->objects);
484 
485 	pw_map_clear(&context->globals);
486 
487 	spa_hook_list_clean(&context->listener_list);
488 	spa_hook_list_clean(&context->driver_listener_list);
489 
490 	free(context);
491 }
492 
493 SPA_EXPORT
pw_context_get_user_data(struct pw_context * context)494 void *pw_context_get_user_data(struct pw_context *context)
495 {
496 	return context->user_data;
497 }
498 
499 SPA_EXPORT
pw_context_add_listener(struct pw_context * context,struct spa_hook * listener,const struct pw_context_events * events,void * data)500 void pw_context_add_listener(struct pw_context *context,
501 			  struct spa_hook *listener,
502 			  const struct pw_context_events *events,
503 			  void *data)
504 {
505 	spa_hook_list_append(&context->listener_list, listener, events, data);
506 }
507 
508 SPA_EXPORT
pw_context_get_support(struct pw_context * context,uint32_t * n_support)509 const struct spa_support *pw_context_get_support(struct pw_context *context, uint32_t *n_support)
510 {
511 	*n_support = context->n_support;
512 	return context->support;
513 }
514 
515 SPA_EXPORT
pw_context_get_main_loop(struct pw_context * context)516 struct pw_loop *pw_context_get_main_loop(struct pw_context *context)
517 {
518 	return context->main_loop;
519 }
520 
521 SPA_EXPORT
pw_context_get_work_queue(struct pw_context * context)522 struct pw_work_queue *pw_context_get_work_queue(struct pw_context *context)
523 {
524 	if (context->work_queue == NULL)
525 		context->work_queue = pw_work_queue_new(context->main_loop);
526 	return context->work_queue;
527 }
528 
529 SPA_EXPORT
pw_context_get_properties(struct pw_context * context)530 const struct pw_properties *pw_context_get_properties(struct pw_context *context)
531 {
532 	return context->properties;
533 }
534 
535 SPA_EXPORT
pw_context_get_conf_section(struct pw_context * context,const char * section)536 const char *pw_context_get_conf_section(struct pw_context *context, const char *section)
537 {
538 	return pw_properties_get(context->conf, section);
539 }
540 
541 /** Update context properties
542  *
543  * \param context a context
544  * \param dict properties to update
545  *
546  * Update the context object with the given properties
547  */
548 SPA_EXPORT
pw_context_update_properties(struct pw_context * context,const struct spa_dict * dict)549 int pw_context_update_properties(struct pw_context *context, const struct spa_dict *dict)
550 {
551 	int changed;
552 
553 	changed = pw_properties_update(context->properties, dict);
554 	pw_log_debug("%p: updated %d properties", context, changed);
555 
556 	return changed;
557 }
558 
global_can_read(struct pw_context * context,struct pw_global * global)559 static bool global_can_read(struct pw_context *context, struct pw_global *global)
560 {
561 	if (context->current_client &&
562 	    !PW_PERM_IS_R(pw_global_get_permissions(global, context->current_client)))
563 		return false;
564 	return true;
565 }
566 
567 SPA_EXPORT
pw_context_for_each_global(struct pw_context * context,int (* callback)(void * data,struct pw_global * global),void * data)568 int pw_context_for_each_global(struct pw_context *context,
569 			    int (*callback) (void *data, struct pw_global *global),
570 			    void *data)
571 {
572 	struct pw_global *g, *t;
573 	int res;
574 
575 	spa_list_for_each_safe(g, t, &context->global_list, link) {
576 		if (!global_can_read(context, g))
577 			continue;
578 		if ((res = callback(data, g)) != 0)
579 			return res;
580 	}
581 	return 0;
582 }
583 
584 SPA_EXPORT
pw_context_find_global(struct pw_context * context,uint32_t id)585 struct pw_global *pw_context_find_global(struct pw_context *context, uint32_t id)
586 {
587 	struct pw_global *global;
588 
589 	global = pw_map_lookup(&context->globals, id);
590 	if (global == NULL || !global->registered) {
591 		errno = ENOENT;
592 		return NULL;
593 	}
594 
595 	if (!global_can_read(context, global)) {
596 		errno = EACCES;
597 		return NULL;
598 	}
599 	return global;
600 }
601 
602 /** Find a port to link with
603  *
604  * \param context a context
605  * \param other_port a port to find a link with
606  * \param id the id of a port or PW_ID_ANY
607  * \param props extra properties
608  * \param n_format_filters number of filters
609  * \param format_filters array of format filters
610  * \param[out] error an error when something is wrong
611  * \return a port that can be used to link to \a otherport or NULL on error
612  */
pw_context_find_port(struct pw_context * context,struct pw_impl_port * other_port,uint32_t id,struct pw_properties * props,uint32_t n_format_filters,struct spa_pod ** format_filters,char ** error)613 struct pw_impl_port *pw_context_find_port(struct pw_context *context,
614 				  struct pw_impl_port *other_port,
615 				  uint32_t id,
616 				  struct pw_properties *props,
617 				  uint32_t n_format_filters,
618 				  struct spa_pod **format_filters,
619 				  char **error)
620 {
621 	struct pw_impl_port *best = NULL;
622 	bool have_id;
623 	struct pw_impl_node *n;
624 
625 	have_id = id != PW_ID_ANY;
626 
627 	pw_log_debug("%p: id:%u", context, id);
628 
629 	spa_list_for_each(n, &context->node_list, link) {
630 		if (n->global == NULL)
631 			continue;
632 
633 		if (other_port->node == n)
634 			continue;
635 
636 		if (!global_can_read(context, n->global))
637 			continue;
638 
639 		pw_log_debug("%p: node id:%d", context, n->global->id);
640 
641 		if (have_id) {
642 			if (n->global->id == id) {
643 				pw_log_debug("%p: id:%u matches node %p", context, id, n);
644 
645 				best =
646 				    pw_impl_node_find_port(n,
647 						pw_direction_reverse(other_port->direction),
648 						PW_ID_ANY);
649 				if (best)
650 					break;
651 			}
652 		} else {
653 			struct pw_impl_port *p, *pin, *pout;
654 			uint8_t buf[4096];
655 			struct spa_pod_builder b = SPA_POD_BUILDER_INIT(buf, sizeof(buf));
656 			struct spa_pod *dummy;
657 
658 			p = pw_impl_node_find_port(n,
659 					pw_direction_reverse(other_port->direction),
660 					PW_ID_ANY);
661 			if (p == NULL)
662 				continue;
663 
664 			if (p->direction == PW_DIRECTION_OUTPUT) {
665 				pin = other_port;
666 				pout = p;
667 			} else {
668 				pin = p;
669 				pout = other_port;
670 			}
671 
672 			if (pw_context_find_format(context,
673 						pout,
674 						pin,
675 						props,
676 						n_format_filters,
677 						format_filters,
678 						&dummy,
679 						&b,
680 						error) < 0) {
681 				free(*error);
682 				continue;
683 			}
684 			best = p;
685 			break;
686 		}
687 	}
688 	if (best == NULL) {
689 		*error = spa_aprintf("No matching Node found");
690 	}
691 	return best;
692 }
693 
pw_context_debug_port_params(struct pw_context * this,struct spa_node * node,enum spa_direction direction,uint32_t port_id,uint32_t id,int err,const char * debug,...)694 SPA_PRINTF_FUNC(7, 8) int pw_context_debug_port_params(struct pw_context *this,
695 		struct spa_node *node, enum spa_direction direction,
696 		uint32_t port_id, uint32_t id, int err, const char *debug, ...)
697 {
698 	struct spa_pod_builder b = { 0 };
699 	uint8_t buffer[4096];
700 	uint32_t state;
701 	struct spa_pod *param;
702 	int res;
703 	va_list args;
704 
705 	va_start(args, debug);
706 	vsnprintf((char*)buffer, sizeof(buffer), debug, args);
707 	va_end(args);
708 
709 	pw_log_error("params %s: %d:%d %s (%s)",
710 			spa_debug_type_find_name(spa_type_param, id),
711 			direction, port_id, spa_strerror(err), buffer);
712 
713 	if (err == -EBUSY)
714 		return 0;
715 
716         state = 0;
717         while (true) {
718                 spa_pod_builder_init(&b, buffer, sizeof(buffer));
719                 res = spa_node_port_enum_params_sync(node,
720                                        direction, port_id,
721                                        id, &state,
722                                        NULL, &param, &b);
723                 if (res != 1) {
724 			if (res < 0)
725 				pw_log_error("  error: %s", spa_strerror(res));
726                         break;
727 		}
728                 pw_log_pod(SPA_LOG_LEVEL_ERROR, param);
729         }
730         return 0;
731 }
732 
733 /** Find a common format between two ports
734  *
735  * \param context a context object
736  * \param output an output port
737  * \param input an input port
738  * \param props extra properties
739  * \param n_format_filters number of format filters
740  * \param format_filters array of format filters
741  * \param[out] format the common format between the ports
742  * \param builder builder to use for processing
743  * \param[out] error an error when something is wrong
744  * \return a common format of NULL on error
745  *
746  * Find a common format between the given ports. The format will
747  * be restricted to a subset given with the format filters.
748  */
pw_context_find_format(struct pw_context * context,struct pw_impl_port * output,struct pw_impl_port * input,struct pw_properties * props,uint32_t n_format_filters,struct spa_pod ** format_filters,struct spa_pod ** format,struct spa_pod_builder * builder,char ** error)749 int pw_context_find_format(struct pw_context *context,
750 			struct pw_impl_port *output,
751 			struct pw_impl_port *input,
752 			struct pw_properties *props,
753 			uint32_t n_format_filters,
754 			struct spa_pod **format_filters,
755 			struct spa_pod **format,
756 			struct spa_pod_builder *builder,
757 			char **error)
758 {
759 	uint32_t out_state, in_state;
760 	int res;
761 	uint32_t iidx = 0, oidx = 0;
762 	struct spa_pod_builder fb = { 0 };
763 	uint8_t fbuf[4096];
764 	struct spa_pod *filter;
765 
766 	out_state = output->state;
767 	in_state = input->state;
768 
769 	pw_log_debug("%p: finding best format %d %d", context, out_state, in_state);
770 
771 	/* when a port is configured but the node is idle, we can reconfigure with a different format */
772 	if (out_state > PW_IMPL_PORT_STATE_CONFIGURE && output->node->info.state == PW_NODE_STATE_IDLE)
773 		out_state = PW_IMPL_PORT_STATE_CONFIGURE;
774 	if (in_state > PW_IMPL_PORT_STATE_CONFIGURE && input->node->info.state == PW_NODE_STATE_IDLE)
775 		in_state = PW_IMPL_PORT_STATE_CONFIGURE;
776 
777 	pw_log_debug("%p: states %d %d", context, out_state, in_state);
778 
779 	if (in_state == PW_IMPL_PORT_STATE_CONFIGURE && out_state > PW_IMPL_PORT_STATE_CONFIGURE) {
780 		/* only input needs format */
781 		spa_pod_builder_init(&fb, fbuf, sizeof(fbuf));
782 		if ((res = spa_node_port_enum_params_sync(output->node->node,
783 						     output->direction, output->port_id,
784 						     SPA_PARAM_Format, &oidx,
785 						     NULL, &filter, &fb)) != 1) {
786 			if (res < 0)
787 				*error = spa_aprintf("error get output format: %s", spa_strerror(res));
788 			else
789 				*error = spa_aprintf("no output formats");
790 			goto error;
791 		}
792 		pw_log_debug("%p: Got output format:", context);
793 		pw_log_format(SPA_LOG_LEVEL_DEBUG, filter);
794 
795 		if ((res = spa_node_port_enum_params_sync(input->node->node,
796 						     input->direction, input->port_id,
797 						     SPA_PARAM_EnumFormat, &iidx,
798 						     filter, format, builder)) <= 0) {
799 			if (res == -ENOENT || res == 0) {
800 				pw_log_debug("%p: no input format filter, using output format: %s",
801 						context, spa_strerror(res));
802 				*format = filter;
803 			} else {
804 				*error = spa_aprintf("error input enum formats: %s", spa_strerror(res));
805 				goto error;
806 			}
807 		}
808 	} else if (out_state >= PW_IMPL_PORT_STATE_CONFIGURE && in_state > PW_IMPL_PORT_STATE_CONFIGURE) {
809 		/* only output needs format */
810 		spa_pod_builder_init(&fb, fbuf, sizeof(fbuf));
811 		if ((res = spa_node_port_enum_params_sync(input->node->node,
812 						     input->direction, input->port_id,
813 						     SPA_PARAM_Format, &iidx,
814 						     NULL, &filter, &fb)) != 1) {
815 			if (res < 0)
816 				*error = spa_aprintf("error get input format: %s", spa_strerror(res));
817 			else
818 				*error = spa_aprintf("no input format");
819 			goto error;
820 		}
821 		pw_log_debug("%p: Got input format:", context);
822 		pw_log_format(SPA_LOG_LEVEL_DEBUG, filter);
823 
824 		if ((res = spa_node_port_enum_params_sync(output->node->node,
825 						     output->direction, output->port_id,
826 						     SPA_PARAM_EnumFormat, &oidx,
827 						     filter, format, builder)) <= 0) {
828 			if (res == -ENOENT || res == 0) {
829 				pw_log_debug("%p: no output format filter, using input format: %s",
830 						context, spa_strerror(res));
831 				*format = filter;
832 			} else {
833 				*error = spa_aprintf("error output enum formats: %s", spa_strerror(res));
834 				goto error;
835 			}
836 		}
837 	} else if (in_state == PW_IMPL_PORT_STATE_CONFIGURE && out_state == PW_IMPL_PORT_STATE_CONFIGURE) {
838 	      again:
839 		/* both ports need a format */
840 		pw_log_debug("%p: do enum input %d", context, iidx);
841 		spa_pod_builder_init(&fb, fbuf, sizeof(fbuf));
842 		if ((res = spa_node_port_enum_params_sync(input->node->node,
843 						     input->direction, input->port_id,
844 						     SPA_PARAM_EnumFormat, &iidx,
845 						     NULL, &filter, &fb)) != 1) {
846 			if (res == -ENOENT) {
847 				pw_log_debug("%p: no input filter", context);
848 				filter = NULL;
849 			} else {
850 				if (res < 0)
851 					*error = spa_aprintf("error input enum formats: %s", spa_strerror(res));
852 				else
853 					*error = spa_aprintf("no more input formats");
854 				goto error;
855 			}
856 		}
857 		pw_log_debug("%p: enum output %d with filter: %p", context, oidx, filter);
858 		pw_log_format(SPA_LOG_LEVEL_DEBUG, filter);
859 
860 		if ((res = spa_node_port_enum_params_sync(output->node->node,
861 						     output->direction, output->port_id,
862 						     SPA_PARAM_EnumFormat, &oidx,
863 						     filter, format, builder)) != 1) {
864 			if (res == 0 && filter != NULL) {
865 				oidx = 0;
866 				goto again;
867 			}
868 			*error = spa_aprintf("error output enum formats: %s", spa_strerror(res));
869 			goto error;
870 		}
871 
872 		pw_log_debug("%p: Got filtered:", context);
873 		pw_log_format(SPA_LOG_LEVEL_DEBUG, *format);
874 	} else {
875 		res = -EBADF;
876 		*error = spa_aprintf("error bad node state");
877 		goto error;
878 	}
879 	return res;
880 error:
881 	if (res == 0)
882 		res = -EINVAL;
883 	return res;
884 }
885 
ensure_state(struct pw_impl_node * node,bool running)886 static int ensure_state(struct pw_impl_node *node, bool running)
887 {
888 	enum pw_node_state state = node->info.state;
889 	if (node->active && !SPA_FLAG_IS_SET(node->spa_flags, SPA_NODE_FLAG_NEED_CONFIGURE) && running)
890 		state = PW_NODE_STATE_RUNNING;
891 	else if (state > PW_NODE_STATE_IDLE)
892 		state = PW_NODE_STATE_IDLE;
893 	return pw_impl_node_set_state(node, state);
894 }
895 
collect_nodes(struct pw_context * context,struct pw_impl_node * driver)896 static int collect_nodes(struct pw_context *context, struct pw_impl_node *driver)
897 {
898 	struct spa_list queue;
899 	struct pw_impl_node *n, *t;
900 	struct pw_impl_port *p;
901 	struct pw_impl_link *l;
902 
903 	spa_list_consume(t, &driver->follower_list, follower_link) {
904 		spa_list_remove(&t->follower_link);
905 		spa_list_init(&t->follower_link);
906 	}
907 
908 	pw_log_debug("driver %p: '%s'", driver, driver->name);
909 
910 	/* start with driver in the queue */
911 	spa_list_init(&queue);
912 	spa_list_append(&queue, &driver->sort_link);
913 	driver->visited = true;
914 
915 	/* now follow all the links from the nodes in the queue
916 	 * and add the peers to the queue. */
917 	spa_list_consume(n, &queue, sort_link) {
918 		spa_list_remove(&n->sort_link);
919 		pw_impl_node_set_driver(n, driver);
920 		n->passive = true;
921 
922 		spa_list_for_each(p, &n->input_ports, link) {
923 			spa_list_for_each(l, &p->links, input_link) {
924 				t = l->output->node;
925 
926 				if (!t->active)
927 					continue;
928 
929 				pw_impl_link_prepare(l);
930 
931 				if (!l->prepared)
932 					continue;
933 
934 				if (!l->passive)
935 					driver->passive = n->passive = false;
936 
937 				if (!t->visited) {
938 					t->visited = true;
939 					spa_list_append(&queue, &t->sort_link);
940 				}
941 			}
942 		}
943 		spa_list_for_each(p, &n->output_ports, link) {
944 			spa_list_for_each(l, &p->links, output_link) {
945 				t = l->input->node;
946 
947 				if (!t->active)
948 					continue;
949 
950 				pw_impl_link_prepare(l);
951 
952 				if (!l->prepared)
953 					continue;
954 
955 				if (!l->passive)
956 					driver->passive = n->passive = false;
957 
958 				if (!t->visited) {
959 					t->visited = true;
960 					spa_list_append(&queue, &t->sort_link);
961 				}
962 			}
963 		}
964 		/* now go through all the nodes that have the same group and
965 		 * that are not yet visited */
966 		if (n->group[0] == '\0')
967 			continue;
968 
969 		spa_list_for_each(t, &context->node_list, link) {
970 			if (t->exported || t == n || !t->active || t->visited)
971 				continue;
972 			if (!spa_streq(t->group, n->group))
973 				continue;
974 			pw_log_debug("%p join group %s: '%s'", t, t->group, n->group);
975 			t->visited = true;
976 			spa_list_append(&queue, &t->sort_link);
977 		}
978 	}
979 	return 0;
980 }
981 
get_quantums(struct pw_context * context,uint32_t * def,uint32_t * min,uint32_t * max,uint32_t * rate)982 static inline void get_quantums(struct pw_context *context, uint32_t *def,
983 		uint32_t *min, uint32_t *max, uint32_t *rate)
984 {
985 	struct settings *s = &context->settings;
986 	*def = s->clock_force_quantum == 0 ? s->clock_quantum : s->clock_force_quantum;
987 	*min = s->clock_force_quantum == 0 ? s->clock_min_quantum : s->clock_force_quantum;
988 	*max = s->clock_force_quantum == 0 ? s->clock_max_quantum : s->clock_force_quantum;
989 	*rate = s->clock_force_quantum == 0 ? s->clock_rate : s->clock_force_rate;
990 }
991 
get_rates(struct pw_context * context,uint32_t * def,uint32_t * n_rates,bool * force_rate)992 static inline uint32_t *get_rates(struct pw_context *context, uint32_t *def, uint32_t *n_rates,
993 		bool *force_rate)
994 {
995 	struct settings *s = &context->settings;
996 	if (s->clock_force_rate != 0) {
997 		*force_rate = true;
998 		*n_rates = 1;
999 		*def = s->clock_force_rate;
1000 		return &s->clock_force_rate;
1001 	} else {
1002 		*force_rate = false;
1003 		*n_rates = s->n_clock_rates;
1004 		*def = s->clock_rate;
1005 		return s->clock_rates;
1006 	}
1007 }
suspend_driver(struct pw_context * context,struct pw_impl_node * n)1008 static void suspend_driver(struct pw_context *context, struct pw_impl_node *n)
1009 {
1010 	struct pw_impl_node *s;
1011 
1012 	spa_list_for_each(s, &n->follower_list, follower_link) {
1013 		if (s == n)
1014 			continue;
1015 		pw_log_debug("%p: follower %p: '%s' suspend",
1016 				context, s, s->name);
1017 		pw_impl_node_set_state(s, PW_NODE_STATE_SUSPENDED);
1018 	}
1019 	pw_log_debug("%p: driver %p: '%s' suspend",
1020 			context, n, n->name);
1021 	pw_impl_node_set_state(n, PW_NODE_STATE_SUSPENDED);
1022 }
1023 
1024 /* find smaller power of 2 */
flp2(uint32_t x)1025 static uint32_t flp2(uint32_t x)
1026 {
1027 	x = x | (x >> 1);
1028 	x = x | (x >> 2);
1029 	x = x | (x >> 4);
1030 	x = x | (x >> 8);
1031 	x = x | (x >> 16);
1032 	return x - (x >> 1);
1033 }
1034 
1035 /* cmp fractions, avoiding overflows */
fraction_compare(const struct spa_fraction * a,const struct spa_fraction * b)1036 static int fraction_compare(const struct spa_fraction *a, const struct spa_fraction *b)
1037 {
1038 	uint64_t fa = (uint64_t)a->num * (uint64_t)b->denom;
1039 	uint64_t fb = (uint64_t)b->num * (uint64_t)a->denom;
1040 	return fa < fb ? -1 : (fa > fb ? 1 : 0);
1041 }
1042 
rates_contains(uint32_t * rates,uint32_t n_rates,uint32_t rate)1043 static bool rates_contains(uint32_t *rates, uint32_t n_rates, uint32_t rate)
1044 {
1045 	uint32_t i;
1046 	for (i = 0; i < n_rates; i++)
1047 		if (rates[i] == rate)
1048 			return true;
1049 	return false;
1050 }
1051 
pw_context_recalc_graph(struct pw_context * context,const char * reason)1052 int pw_context_recalc_graph(struct pw_context *context, const char *reason)
1053 {
1054 	struct impl *impl = SPA_CONTAINER_OF(context, struct impl, this);
1055 	struct pw_impl_node *n, *s, *target, *fallback;
1056 	uint32_t max_quantum, min_quantum, def_quantum, rate_quantum;
1057 	uint32_t *rates, n_rates, def_rate;
1058 	bool freewheel = false, force_rate;
1059 
1060 	pw_log_info("%p: busy:%d reason:%s", context, impl->recalc, reason);
1061 
1062 	if (impl->recalc) {
1063 		impl->recalc_pending = true;
1064 		return -EBUSY;
1065 	}
1066 
1067 again:
1068 	impl->recalc = true;
1069 
1070 	get_quantums(context, &def_quantum, &min_quantum, &max_quantum, &rate_quantum);
1071 	rates = get_rates(context, &def_rate, &n_rates, &force_rate);
1072 
1073 	/* start from all drivers and group all nodes that are linked
1074 	 * to it. Some nodes are not (yet) linked to anything and they
1075 	 * will end up 'unassigned' to a driver. Other nodes are drivers
1076 	 * and if they have active followers, we can use them to schedule
1077 	 * the unassigned nodes. */
1078 	target = fallback = NULL;
1079 	spa_list_for_each(n, &context->driver_list, driver_link) {
1080 		if (n->exported)
1081 			continue;
1082 
1083 		if (!n->visited)
1084 			collect_nodes(context, n);
1085 
1086 		/* from now on we are only interested in active driving nodes.
1087 		 * We're going to see if there are active followers. */
1088 		if (!n->driving || !n->active)
1089 			continue;
1090 
1091 		/* first active driving node is fallback */
1092 		if (fallback == NULL)
1093 			fallback = n;
1094 
1095 		if (n->passive)
1096 			continue;
1097 
1098 		spa_list_for_each(s, &n->follower_list, follower_link) {
1099 			pw_log_debug("%p: driver %p: follower %p %s: active:%d",
1100 					context, n, s, s->name, s->active);
1101 			if (s != n && s->active) {
1102 				/* if the driving node has active followers, it
1103 				 * is a target for our unassigned nodes */
1104 				if (target == NULL)
1105 					target = n;
1106 				if (n->freewheel)
1107 					freewheel = true;
1108 				break;
1109 			}
1110 		}
1111 	}
1112 	/* no active node, use fallback driving node */
1113 	if (target == NULL)
1114 		target = fallback;
1115 
1116 	/* update the freewheel status */
1117 	if (context->freewheeling != freewheel)
1118 		context_set_freewheel(context, freewheel);
1119 
1120 	/* now go through all available nodes. The ones we didn't visit
1121 	 * in collect_nodes() are not linked to any driver. We assign them
1122 	 * to either an active driver of the first driver */
1123 	spa_list_for_each(n, &context->node_list, link) {
1124 		if (n->exported)
1125 			continue;
1126 
1127 		if (!n->visited) {
1128 			struct pw_impl_node *t;
1129 
1130 			pw_log_debug("%p: unassigned node %p: '%s' active:%d want_driver:%d target:%p",
1131 					context, n, n->name, n->active, n->want_driver, target);
1132 
1133 			t = n->want_driver ? target : NULL;
1134 
1135 			pw_impl_node_set_driver(n, t);
1136 			if (t == NULL)
1137 				ensure_state(n, false);
1138 			else
1139 				t->passive = false;
1140 		}
1141 		n->visited = false;
1142 	}
1143 
1144 	/* assign final quantum and set state for followers and drivers */
1145 	spa_list_for_each(n, &context->driver_list, driver_link) {
1146 		bool running = false, lock_quantum = false, lock_rate = false;
1147 		struct spa_fraction latency = SPA_FRACTION(0, 0);
1148 		struct spa_fraction max_latency = SPA_FRACTION(0, 0);
1149 		struct spa_fraction rate = SPA_FRACTION(0, 0);
1150 		uint32_t quantum, target_rate, current_rate;
1151 
1152 		if (!n->driving || n->exported)
1153 			continue;
1154 
1155 		/* collect quantum and rate */
1156 		spa_list_for_each(s, &n->follower_list, follower_link) {
1157 
1158 			if (s->info.state > PW_NODE_STATE_SUSPENDED) {
1159 				lock_quantum |= s->lock_quantum;
1160 				lock_rate |= s->lock_rate;
1161 			}
1162 
1163 			/* smallest latencies */
1164 			if (latency.denom == 0 ||
1165 			    (s->latency.denom > 0 &&
1166 			     fraction_compare(&s->latency, &latency) < 0))
1167 				latency = s->latency;
1168 			if (max_latency.denom == 0 ||
1169 			    (s->max_latency.denom > 0 &&
1170 			     fraction_compare(&s->max_latency, &max_latency) < 0))
1171 				max_latency = s->max_latency;
1172 
1173 			/* largest rate */
1174 			if (rate.denom == 0 ||
1175 			    (s->rate.denom > 0 &&
1176 			     fraction_compare(&s->rate, &rate) > 0))
1177 				rate = s->rate;
1178 
1179 			if (s->active)
1180 				running = !n->passive;
1181 		}
1182 
1183 		if (force_rate)
1184 			lock_rate = false;
1185 
1186 		current_rate = n->current_rate.denom;
1187 		if (lock_rate ||
1188 		    (!force_rate &&
1189 		    (n->info.state > PW_NODE_STATE_IDLE)))
1190 			target_rate = current_rate;
1191 		else {
1192 			/* calculate desired rate */
1193 			target_rate = def_rate;
1194 			if (rate.denom != 0 && rate.num == 1) {
1195 				if (rates_contains(rates, n_rates, rate.denom))
1196 					target_rate = rate.denom;
1197 			}
1198 		}
1199 
1200 		if (target_rate != current_rate) {
1201 			pw_log_info("(%s-%u) state:%s new rate:%u->%u",
1202 					n->name, n->info.id,
1203 					pw_node_state_as_string(n->info.state),
1204 					n->current_rate.denom,
1205 					target_rate);
1206 
1207 			if (force_rate) {
1208 				if (context->settings.clock_rate_update_mode == CLOCK_RATE_UPDATE_MODE_HARD)
1209 					suspend_driver(context, n);
1210 			} else {
1211 				if (n->info.state >= PW_NODE_STATE_IDLE)
1212 					suspend_driver(context, n);
1213 			}
1214 			n->current_rate = SPA_FRACTION(1, target_rate);
1215 			n->current_pending = true;
1216 			current_rate = target_rate;
1217 			/* we might be suspended now and the links need to be prepared again */
1218 			goto again;
1219 		}
1220 
1221 		if (rate_quantum != 0 && current_rate != rate_quantum) {
1222 			def_quantum = def_quantum * current_rate / rate_quantum;
1223 			min_quantum = min_quantum * current_rate / rate_quantum;
1224 		}
1225 
1226 		/* calculate desired quantum */
1227 		if (max_latency.denom != 0) {
1228 			uint32_t tmp = (max_latency.num * current_rate / max_latency.denom);
1229 			if (tmp < max_quantum)
1230 				max_quantum = tmp;
1231 		}
1232 
1233 		quantum = def_quantum;
1234 		if (latency.denom != 0)
1235 			quantum = (latency.num * current_rate / latency.denom);
1236 		quantum = SPA_CLAMP(quantum, min_quantum, max_quantum);
1237 
1238 		if (context->settings.clock_power_of_two_quantum)
1239 			quantum = flp2(quantum);
1240 
1241 		if (running && quantum != n->current_quantum && !lock_quantum) {
1242 			pw_log_info("(%s-%u) new quantum:%"PRIu64"->%u",
1243 					n->name, n->info.id,
1244 					n->current_quantum,
1245 					quantum);
1246 			n->current_quantum = quantum;
1247 			n->current_pending = true;
1248 		}
1249 
1250 		if (n->info.state < PW_NODE_STATE_RUNNING && n->current_pending) {
1251 			n->rt.position->clock.duration = n->current_quantum;
1252 			n->rt.position->clock.rate = n->current_rate;
1253 			n->current_pending = false;
1254 		}
1255 
1256 		pw_log_debug("%p: driving %p running:%d passive:%d quantum:%u '%s'",
1257 				context, n, running, n->passive, quantum, n->name);
1258 
1259 		spa_list_for_each(s, &n->follower_list, follower_link) {
1260 			if (s == n)
1261 				continue;
1262 			pw_log_debug("%p: follower %p: active:%d '%s'",
1263 					context, s, s->active, s->name);
1264 			ensure_state(s, running);
1265 		}
1266 		ensure_state(n, running);
1267 	}
1268 	impl->recalc = false;
1269 	if (impl->recalc_pending) {
1270 		impl->recalc_pending = false;
1271 		goto again;
1272 	}
1273 
1274 	return 0;
1275 }
1276 
1277 SPA_EXPORT
pw_context_add_spa_lib(struct pw_context * context,const char * factory_regexp,const char * lib)1278 int pw_context_add_spa_lib(struct pw_context *context,
1279 		const char *factory_regexp, const char *lib)
1280 {
1281 	struct factory_entry *entry;
1282 	int err;
1283 
1284 	entry = pw_array_add(&context->factory_lib, sizeof(*entry));
1285 	if (entry == NULL)
1286 		return -errno;
1287 
1288 	if ((err = regcomp(&entry->regex, factory_regexp, REG_EXTENDED | REG_NOSUB)) != 0) {
1289 		char errbuf[1024];
1290 		regerror(err, &entry->regex, errbuf, sizeof(errbuf));
1291 		pw_log_error("%p: can compile regex: %s", context, errbuf);
1292 		pw_array_remove(&context->factory_lib, entry);
1293 		return -EINVAL;
1294 	}
1295 
1296 	entry->lib = strdup(lib);
1297 	pw_log_debug("%p: map factory regex '%s' to '%s", context,
1298 			factory_regexp, lib);
1299 	return 0;
1300 }
1301 
1302 SPA_EXPORT
pw_context_find_spa_lib(struct pw_context * context,const char * factory_name)1303 const char *pw_context_find_spa_lib(struct pw_context *context, const char *factory_name)
1304 {
1305 	struct factory_entry *entry;
1306 
1307 	pw_array_for_each(entry, &context->factory_lib) {
1308 		if (regexec(&entry->regex, factory_name, 0, NULL, 0) == 0)
1309 			return entry->lib;
1310 	}
1311 	return NULL;
1312 }
1313 
1314 SPA_EXPORT
pw_context_load_spa_handle(struct pw_context * context,const char * factory_name,const struct spa_dict * info)1315 struct spa_handle *pw_context_load_spa_handle(struct pw_context *context,
1316 		const char *factory_name,
1317 		const struct spa_dict *info)
1318 {
1319 	const char *lib;
1320 	const struct spa_support *support;
1321 	uint32_t n_support;
1322 	struct spa_handle *handle;
1323 
1324 	pw_log_debug("%p: load factory %s", context, factory_name);
1325 
1326 	lib = pw_context_find_spa_lib(context, factory_name);
1327 	if (lib == NULL && info != NULL)
1328 		lib = spa_dict_lookup(info, SPA_KEY_LIBRARY_NAME);
1329 	if (lib == NULL) {
1330 		errno = ENOENT;
1331 		pw_log_warn("%p: no library for %s: %m",
1332 				context, factory_name);
1333 		return NULL;
1334 	}
1335 
1336 	support = pw_context_get_support(context, &n_support);
1337 
1338 	handle = pw_load_spa_handle(lib, factory_name,
1339 			info, n_support, support);
1340 
1341 	return handle;
1342 }
1343 
1344 SPA_EXPORT
pw_context_register_export_type(struct pw_context * context,struct pw_export_type * type)1345 int pw_context_register_export_type(struct pw_context *context, struct pw_export_type *type)
1346 {
1347 	if (pw_context_find_export_type(context, type->type)) {
1348 		pw_log_warn("context %p: duplicate export type %s", context, type->type);
1349 		return -EEXIST;
1350 	}
1351 	pw_log_debug("context %p: Add export type %s to context", context, type->type);
1352 	spa_list_append(&context->export_list, &type->link);
1353 	return 0;
1354 }
1355 
1356 SPA_EXPORT
pw_context_find_export_type(struct pw_context * context,const char * type)1357 const struct pw_export_type *pw_context_find_export_type(struct pw_context *context, const char *type)
1358 {
1359 	const struct pw_export_type *t;
1360 	spa_list_for_each(t, &context->export_list, link) {
1361 		if (spa_streq(t->type, type))
1362 			return t;
1363 	}
1364 	return NULL;
1365 }
1366 
1367 struct object_entry {
1368 	const char *type;
1369 	void *value;
1370 };
1371 
find_object(struct pw_context * context,const char * type)1372 static struct object_entry *find_object(struct pw_context *context, const char *type)
1373 {
1374 	struct object_entry *entry;
1375 	pw_array_for_each(entry, &context->objects) {
1376 		if (spa_streq(entry->type, type))
1377 			return entry;
1378 	}
1379 	return NULL;
1380 }
1381 
1382 SPA_EXPORT
pw_context_set_object(struct pw_context * context,const char * type,void * value)1383 int pw_context_set_object(struct pw_context *context, const char *type, void *value)
1384 {
1385 	struct object_entry *entry;
1386 
1387 	entry = find_object(context, type);
1388 
1389 	if (value == NULL) {
1390 		if (entry)
1391 			pw_array_remove(&context->objects, entry);
1392 	} else {
1393 		if (entry == NULL) {
1394 			entry = pw_array_add(&context->objects, sizeof(*entry));
1395 			if (entry == NULL)
1396 				return -errno;
1397 			entry->type = type;
1398 		}
1399 		entry->value = value;
1400 	}
1401 	return 0;
1402 }
1403 
1404 SPA_EXPORT
pw_context_get_object(struct pw_context * context,const char * type)1405 void *pw_context_get_object(struct pw_context *context, const char *type)
1406 {
1407 	struct object_entry *entry;
1408 
1409 	if ((entry = find_object(context, type)) != NULL)
1410 		return entry->value;
1411 
1412 	return NULL;
1413 }
1414