2 * module-murphy-ivi -- PulseAudio module for providing audio routing support
3 * Copyright (c) 2012, Intel Corporation.
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU Lesser General Public License,
7 * version 2.1, as published by the Free Software Foundation.
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.
12 * See the GNU Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this program; if not, write to the
16 * Free Software Foundation, Inc., 51 Franklin St - Fifth Floor, Boston,
20 #include <sys/types.h>
26 #include <pulse/utf8.h>
27 #include <pulse/timeval.h>
28 #include <pulsecore/pulsecore-config.h>
29 #include <pulsecore/module.h>
30 #include <pulsecore/llist.h>
31 #include <pulsecore/idxset.h>
32 #include <pulsecore/hashmap.h>
33 #include <pulsecore/core-util.h>
34 #include <pulsecore/sink-input.h>
35 #include <pulsecore/source-output.h>
39 #define WITH_RESOURCES
42 #if defined(WITH_DOMCTL) || defined(WITH_RESOURCES)
43 #include <murphy/common/macros.h>
44 #include <murphy/common/mainloop.h>
45 #include <murphy/pulse/pulse-glue.h>
49 #include <murphy/resource/protocol.h>
50 #include <murphy/common/transport.h>
51 #include <murphy/resource/protocol.h>
52 #include <murphy/resource/data-types.h>
57 #include "stream-state.h"
61 #define INVALID_ID (~(uint32_t)0)
62 #define INVALID_INDEX (~(uint32_t)0)
63 #define INVALID_SEQNO (~(uint32_t)0)
64 #define INVALID_REQUEST (~(uint16_t)0)
66 #define DISCONNECTED -1
70 #define RESCOL_NAMES "rsetid,autorel,state,grant,pid,policy"
71 #define RESCOL_RSETID 0
72 #define RESCOL_AUTOREL 1
73 #define RESCOL_STATE 2
74 #define RESCOL_GRANT 3
76 #define RESCOL_POLICY 5
78 #define RSET_RELEASE 1
79 #define RSET_ACQUIRE 2
81 #define PUSH_VALUE(msg, tag, typ, val) \
82 mrp_msg_append(msg, MRP_MSG_TAG_##typ(RESPROTO_##tag, val))
84 #define PUSH_ATTRS(msg, rif, proplist) \
85 resource_push_attributes(msg, rif, proplist)
87 typedef struct resource_attribute resource_attribute;
88 typedef struct resource_request resource_request;
90 struct resource_attribute {
91 PA_LLIST_FIELDS(resource_attribute);
96 struct resource_request {
97 PA_LLIST_FIELDS(resource_request);
110 mrp_domctl_table_t *tables;
112 mrp_domctl_watch_t *watches;
113 pa_murphyif_watch_cb watchcb;
124 audio_resource_t inpres;
125 audio_resource_t outres;
126 #ifdef WITH_RESOURCES
127 mrp_transport_t *transp;
128 mrp_sockaddr_t saddr;
144 PA_LLIST_HEAD(resource_attribute, attrs);
145 PA_LLIST_HEAD(resource_request, reqs);
147 } resource_interface;
151 #if defined(WITH_DOMCTL) || defined(WITH_RESOURCES)
154 domctl_interface domctl;
155 resource_interface resource;
158 #ifdef WITH_RESOURCES
183 static void domctl_connect_notify(mrp_domctl_t *,int,int,const char *,void *);
184 static void domctl_watch_notify(mrp_domctl_t *,mrp_domctl_data_t *,int,void *);
185 static void domctl_dump_data(mrp_domctl_data_t *);
188 #ifdef WITH_RESOURCES
189 static void resource_attribute_destroy(resource_interface *,
190 resource_attribute *);
191 static int resource_transport_connect(resource_interface *);
192 static void resource_xport_closed_evt(mrp_transport_t *, int, void *);
194 static mrp_msg_t *resource_create_request(uint32_t, mrp_resproto_request_t);
195 static pa_bool_t resource_send_message(resource_interface *, mrp_msg_t *,
196 uint32_t, uint16_t, uint32_t);
197 static pa_bool_t resource_set_create_node(struct userdata *, mir_node *,
198 pa_nodeset_resdef *, pa_bool_t);
199 static pa_bool_t resource_set_create_all(struct userdata *);
200 static pa_bool_t resource_set_destroy_node(struct userdata *, uint32_t);
201 static pa_bool_t resource_set_destroy_all(struct userdata *);
202 static void resource_set_notification(struct userdata *, const char *,
203 int, mrp_domctl_value_t **);
205 static pa_bool_t resource_push_attributes(mrp_msg_t *, resource_interface *,
208 static void resource_recv_msg(mrp_transport_t *, mrp_msg_t *, void *);
209 static void resource_recvfrom_msg(mrp_transport_t *, mrp_msg_t *,
210 mrp_sockaddr_t *, socklen_t, void *);
211 static void resource_set_create_response(struct userdata *, mir_node *,
212 mrp_msg_t *, void **);
213 static void resource_set_create_response_abort(struct userdata *,
214 mrp_msg_t *, void **);
216 static pa_bool_t resource_fetch_seqno(mrp_msg_t *, void **, uint32_t *);
217 static pa_bool_t resource_fetch_request(mrp_msg_t *, void **, uint16_t *);
218 static pa_bool_t resource_fetch_status(mrp_msg_t *, void **, int *);
219 static pa_bool_t resource_fetch_rset_id(mrp_msg_t *, void **, uint32_t*);
220 static pa_bool_t resource_fetch_rset_state(mrp_msg_t *, void **,
221 mrp_resproto_state_t *);
222 static pa_bool_t resource_fetch_rset_mask(mrp_msg_t *, void **,
223 mrp_resproto_state_t *);
225 static pa_bool_t resource_transport_create(struct userdata *, pa_murphyif *);
226 static void resource_transport_destroy(pa_murphyif *);
228 static void connect_attempt(pa_mainloop_api *, pa_time_event *,
229 const struct timeval *, void *);
230 static void schedule_connect(struct userdata *, resource_interface *);
231 static void cancel_schedule(struct userdata *, resource_interface *);
233 static rset_hash *node_put_rset(struct userdata *, mir_node *, rset_data *);
234 static void node_enforce_resource_policy(struct userdata *, mir_node *,
236 static rset_data *rset_data_dup(rset_data *);
237 static void rset_data_copy(rset_data *, rset_data *);
238 static void rset_data_update(rset_data *, rset_data *);
239 static void rset_data_free(rset_data *);
241 static void pid_hashmap_free(void *, void *);
242 static int pid_hashmap_put(struct userdata *, const char *,
243 mir_node *, rset_data *);
244 static mir_node *pid_hashmap_get_node(struct userdata *, const char *);
245 static rset_data *pid_hashmap_get_rset(struct userdata *, const char *);
246 static mir_node *pid_hashmap_remove_node(struct userdata *, const char *);
247 static rset_data *pid_hashmap_remove_rset(struct userdata *, const char *);
249 static void rset_hashmap_free(void *, void *);
250 static rset_hash *rset_hashmap_put(struct userdata *, const char *, mir_node *);
251 static rset_hash *rset_hashmap_get(struct userdata *u, const char *rsetid);
252 static int rset_hashmap_remove(struct userdata *,const char *,mir_node*);
256 static pa_proplist *get_node_proplist(struct userdata *, mir_node *);
257 static const char *get_node_pid(struct userdata *, mir_node *);
260 pa_murphyif *pa_murphyif_init(struct userdata *u,
261 const char *ctl_addr,
262 const char *res_addr)
264 pa_murphyif *murphyif;
265 domctl_interface *dif;
266 resource_interface *rif;
267 #if defined(WITH_DOMCTL) || defined(WITH_RESOURCES)
270 if (!(ml = mrp_mainloop_pulse_get(u->core->mainloop))) {
271 pa_log_error("Failed to set up murphy mainloop.");
275 #ifdef WITH_RESOURCES
278 murphyif = pa_xnew0(pa_murphyif, 1);
279 dif = &murphyif->domctl;
280 rif = &murphyif->resource;
282 #if defined(WITH_DOMCTL) || defined(WITH_RESOURCES)
286 dif->addr = pa_xstrdup(ctl_addr ? ctl_addr:MRP_DEFAULT_DOMCTL_ADDRESS);
290 rif->addr = pa_xstrdup(res_addr ? res_addr:RESPROTO_DEFAULT_ADDRESS);
291 #ifdef WITH_RESOURCES
292 rif->alen = mrp_transport_resolve(NULL, rif->addr, &rif->saddr,
293 sizeof(rif->saddr), &rif->atype);
294 if (rif->alen <= 0) {
295 pa_log("can't resolve resource transport address '%s'", rif->addr);
298 rif->inpres.tblidx = -1;
299 rif->outres.tblidx = -1;
300 rif->connect.period = 1 * PA_USEC_PER_SEC;
302 if (!resource_transport_create(u, murphyif)) {
303 pa_log("failed to create resource transport");
304 schedule_connect(u, rif);
307 if (resource_transport_connect(rif) == DISCONNECTED)
308 schedule_connect(u, rif);
312 rif->seqno.request = 1;
313 rif->nodes.rsetid = pa_hashmap_new(pa_idxset_string_hash_func,
314 pa_idxset_string_compare_func);
315 rif->nodes.pid = pa_hashmap_new(pa_idxset_string_hash_func,
316 pa_idxset_string_compare_func);
317 PA_LLIST_HEAD_INIT(resource_attribute, rif->attrs);
318 PA_LLIST_HEAD_INIT(resource_request, rif->reqs);
325 void pa_murphyif_done(struct userdata *u)
327 pa_murphyif *murphyif;
328 domctl_interface *dif;
329 resource_interface *rif;
330 #ifdef WITH_RESOURCES
331 resource_attribute *attr, *a;
332 resource_request *req, *r;
335 if (u && (murphyif = u->murphyif)) {
337 mrp_domctl_table_t *t;
338 mrp_domctl_watch_t *w;
341 dif = &murphyif->domctl;
343 mrp_domctl_destroy(dif->ctl);
345 if (dif->ntable > 0 && dif->tables) {
346 for (i = 0; i < dif->ntable; i++) {
348 pa_xfree((void *)t->table);
349 pa_xfree((void *)t->mql_columns);
350 pa_xfree((void *)t->mql_index);
352 pa_xfree(dif->tables);
355 if (dif->nwatch > 0 && dif->watches) {
356 for (i = 0; i < dif->nwatch; i++) {
357 w = dif->watches + i;
358 pa_xfree((void *)w->table);
359 pa_xfree((void *)w->mql_columns);
360 pa_xfree((void *)w->mql_where);
362 pa_xfree(dif->watches);
365 pa_xfree((void *)dif->addr);
368 #ifdef WITH_RESOURCES
369 rif = &murphyif->resource;
371 resource_transport_destroy(murphyif);
373 pa_hashmap_free(rif->nodes.rsetid, rset_hashmap_free, NULL);
374 pa_hashmap_free(rif->nodes.pid, pid_hashmap_free, NULL);
376 PA_LLIST_FOREACH_SAFE(attr, a, rif->attrs)
377 resource_attribute_destroy(rif, attr);
379 PA_LLIST_FOREACH_SAFE(req, r, rif->reqs)
382 pa_xfree((void *)rif->addr);
383 pa_xfree((void *)rif->inpres.name);
384 pa_xfree((void *)rif->outres.name);
386 mrp_mainloop_destroy(murphyif->ml);
393 void pa_murphyif_add_table(struct userdata *u,
398 pa_murphyif *murphyif;
399 domctl_interface *dif;
400 mrp_domctl_table_t *t;
407 pa_assert_se((murphyif = u->murphyif));
409 dif = &murphyif->domctl;
412 size = sizeof(mrp_domctl_table_t) * dif->ntable;
413 t = (dif->tables = pa_xrealloc(dif->tables, size)) + idx;
415 t->table = pa_xstrdup(table);
416 t->mql_columns = pa_xstrdup(columns);
417 t->mql_index = index ? pa_xstrdup(index) : NULL;
420 int pa_murphyif_add_watch(struct userdata *u,
426 pa_murphyif *murphyif;
427 domctl_interface *dif;
428 mrp_domctl_watch_t *w;
435 pa_assert(max_rows > 0 && max_rows < MQI_QUERY_RESULT_MAX);
436 pa_assert_se((murphyif = u->murphyif));
438 dif = &murphyif->domctl;
441 size = sizeof(mrp_domctl_watch_t) * dif->nwatch;
442 w = (dif->watches = pa_xrealloc(dif->watches, size)) + idx;
444 w->table = pa_xstrdup(table);
445 w->mql_columns = pa_xstrdup(columns);
446 w->mql_where = where ? pa_xstrdup(where) : NULL;
447 w->max_rows = max_rows;
452 void pa_murphyif_setup_domainctl(struct userdata *u, pa_murphyif_watch_cb wcb)
454 static const char *name = "pulse";
456 pa_murphyif *murphyif;
457 domctl_interface *dif;
461 pa_assert_se((murphyif = u->murphyif));
463 dif = &murphyif->domctl;
466 if (dif->ntable || dif->nwatch) {
467 dif->ctl = mrp_domctl_create(name, murphyif->ml,
468 dif->tables, dif->ntable,
469 dif->watches, dif->nwatch,
470 domctl_connect_notify,
471 domctl_watch_notify, u);
473 pa_log("failed to create '%s' domain controller", name);
477 if (!mrp_domctl_connect(dif->ctl, dif->addr, 0)) {
478 pa_log("failed to conect to murphyd");
483 pa_log_info("'%s' domain controller sucessfully created", name);
488 void pa_murphyif_add_audio_resource(struct userdata *u,
493 static const char *columns = RESCOL_NAMES;
494 static int maxrow = MQI_QUERY_RESULT_MAX - 1;
496 pa_murphyif *murphyif;
497 resource_interface *rif;
498 audio_resource_t *res;
502 pa_assert(dir == mir_input || dir == mir_output);
505 pa_assert_se((murphyif = u->murphyif));
506 rif = &murphyif->resource;
509 if (dir == mir_input) {
510 if (rif->inpres.name)
511 pa_log("attempt to register playback resource multiple time");
516 if (rif->outres.name)
517 pa_log("attempt to register recording resource multiple time");
523 res->name = pa_xstrdup(name);
525 snprintf(table, sizeof(table), "%s_users", name);
526 res->tblidx = pa_murphyif_add_watch(u, table, columns, NULL, maxrow);
531 void pa_murphyif_add_audio_attribute(struct userdata *u,
534 mqi_data_type_t type,
535 ... ) /* default value */
537 #ifdef WITH_RESOURCES
538 pa_murphyif *murphyif;
539 resource_interface *rif;
540 resource_attribute *attr;
541 mrp_attr_value_t *val;
547 pa_assert(type == mqi_string || type == mqi_integer ||
548 type == mqi_unsignd || type == mqi_floating);
550 pa_assert_se((murphyif = u->murphyif));
551 rif = &murphyif->resource;
553 attr = pa_xnew0(resource_attribute, 1);
554 val = &attr->def.value;
556 attr->prop = pa_xstrdup(propnam);
557 attr->def.name = pa_xstrdup(attrnam);
558 attr->def.type = type;
563 case mqi_string: val->string = pa_xstrdup(va_arg(ap, char *)); break;
564 case mqi_integer: val->integer = va_arg(ap, int32_t); break;
565 case mqi_unsignd: val->unsignd = va_arg(ap, uint32_t); break;
566 case mqi_floating: val->floating = va_arg(ap, double); break;
567 default: attr->def.type = mqi_error; break;
572 if (attr->def.type == mqi_error)
573 resource_attribute_destroy(rif, attr);
575 PA_LLIST_PREPEND(resource_attribute, rif->attrs, attr);
579 void pa_murphyif_create_resource_set(struct userdata *u,
581 pa_nodeset_resdef *resdef)
584 pa_murphyif *murphyif;
585 resource_interface *rif;
590 pa_assert((!node->loop && node->implement == mir_stream) ||
591 ( node->loop && node->implement == mir_device) );
592 pa_assert(node->direction == mir_input || node->direction == mir_output);
593 pa_assert(node->zone);
594 pa_assert(!node->rsetid);
596 pa_assert_se((core = u->core));
598 pa_assert_se((murphyif = u->murphyif));
599 rif = &murphyif->resource;
601 state = resource_transport_connect(rif);
606 resource_set_create_all(u);
610 node->localrset = resource_set_create_node(u, node, resdef, TRUE);
618 void pa_murphyif_destroy_resource_set(struct userdata *u, mir_node *node)
620 pa_murphyif *murphyif;
626 pa_assert_se((murphyif = u->murphyif));
628 if (node->localrset && node->rsetid) {
630 pa_murphyif_delete_node(u, node);
632 rsetid = strtoul(node->rsetid, &e, 10);
634 if (e == node->rsetid || *e) {
635 pa_log("can't destroy resource set: invalid rsetid '%s'",
639 if (rset_hashmap_remove(u, node->rsetid, node) < 0) {
640 pa_log_debug("failed to remove resource set %s from hashmap",
644 if (resource_set_destroy_node(u, rsetid))
645 pa_log_debug("sent resource set %u destruction request", rsetid);
647 pa_log("failed to destroy resourse set %u for node '%s'",
648 rsetid, node->amname);
651 pa_xfree(node->rsetid);
653 node->localrset = FALSE;
659 int pa_murphyif_add_node(struct userdata *u, mir_node *node)
661 #ifdef WITH_RESOURCES
662 pa_murphyif *murphyif;
663 resource_interface *rif;
672 pa_assert_se((murphyif = u->murphyif));
674 rif = &murphyif->resource;
677 pa_log("can't register resource set for node %u '%s'.: missing rsetid",
678 node->paidx, node->amname);
680 else if (pa_streq(node->rsetid, PA_RESOURCE_SET_ID_PID)) {
681 if (!(pid = get_node_pid(u,node)))
682 pa_log("can't obtain PID for node '%s'", node->amname);
684 if (pid_hashmap_put(u, pid, node, NULL) == 0)
687 if ((rset = pid_hashmap_remove_rset(u, pid))) {
688 pa_log_debug("found resource-set %s for node '%s'",
689 rset->id, node->amname);
691 if (node_put_rset(u, node, rset)) {
692 node_enforce_resource_policy(u, node, rset);
693 rset_data_free(rset);
697 pa_log("can't register resource set for node '%s': "
698 "failed to set rsetid", node->amname);
700 rset_data_free(rset);
703 pa_log("can't register resource set for node '%s': "
704 "conflicting pid", node->amname);
709 if ((rh = rset_hashmap_put(u, node->rsetid, node))) {
712 pa_log_debug("enforce policies on node %u '%s' rsetid:%s autorel:%s "
713 "state:%s grant:%s policy:%s", node->paidx, node->amname,
714 rset->id, rset->autorel ? "yes":"no",
715 rset->state == RSET_ACQUIRE ? "acquire":"release",
716 rset->grant ? "yes":"no", rset->policy);
718 node_enforce_resource_policy(u, node, rset);
729 void pa_murphyif_delete_node(struct userdata *u, mir_node *node)
731 #ifdef WITH_RESOURCES
732 pa_murphyif *murphyif;
733 resource_interface *rif;
740 pa_assert_se((murphyif = u->murphyif));
742 rif = &murphyif->resource;
745 if (pa_streq(node->rsetid, PA_RESOURCE_SET_ID_PID)) {
746 if ((pid = get_node_pid(u, node))) {
747 if (node == pid_hashmap_get_node(u, pid))
748 pid_hashmap_remove_node(u, pid);
750 pa_log("pid %s seems to have multiple resource sets. "
751 "Refuse to delete node %u (%s) from hashmap",
752 pid, node->index, node->amname);
757 if (rset_hashmap_remove(u, node->rsetid, node) < 0) {
758 pa_log("failed to remove node '%s' from rset hash", node->amname);
767 static void domctl_connect_notify(mrp_domctl_t *dc, int connected, int errcode,
768 const char *errmsg, void *user_data)
771 MRP_UNUSED(user_data);
774 pa_log_info("Successfully registered to Murphy.");
776 pa_log_error("Domain control Connection to Murphy failed (%d: %s).",
781 static void domctl_watch_notify(mrp_domctl_t *dc, mrp_domctl_data_t *tables,
782 int ntable, void *user_data)
784 struct userdata *u = (struct userdata *)user_data;
785 pa_murphyif *murphyif;
786 domctl_interface *dif;
787 resource_interface *rif;
788 mrp_domctl_data_t *t;
789 mrp_domctl_watch_t *w;
795 pa_assert(ntable > 0);
797 pa_assert_se((murphyif = u->murphyif));
799 dif = &murphyif->domctl;
800 rif = &murphyif->resource;
802 pa_log_info("Received change notification for %d tables.", ntable);
804 for (i = 0; i < ntable; i++) {
809 pa_assert(t->id >= 0);
810 pa_assert(t->id < dif->nwatch);
812 w = dif->watches + t->id;
814 #ifdef WITH_RESOURCES
815 if (t->id == rif->inpres.tblidx || t->id == rif->outres.tblidx) {
816 resource_set_notification(u, w->table, t->nrow, t->rows);
821 dif->watchcb(u, w->table, t->nrow, t->rows);
825 static void domctl_dump_data(mrp_domctl_data_t *table)
827 mrp_domctl_value_t *row;
833 pa_log_debug("Table #%d: %d rows x %d columns", table->id,
834 table->nrow, table->ncolumn);
836 for (i = 0; i < table->nrow; i++) {
837 row = table->rows[i];
841 for (j = 0, t = ""; j < table->ncolumn; j++, t = ", ") {
842 switch (row[j].type) {
843 case MRP_DOMCTL_STRING:
844 l = snprintf(p, n, "%s'%s'", t, row[j].str);
848 case MRP_DOMCTL_INTEGER:
849 l = snprintf(p, n, "%s%d", t, row[j].s32);
853 case MRP_DOMCTL_UNSIGNED:
854 l = snprintf(p, n, "%s%u", t, row[j].u32);
858 case MRP_DOMCTL_DOUBLE:
859 l = snprintf(p, n, "%s%f", t, row[j].dbl);
864 l = snprintf(p, n, "%s<invalid column 0x%x>",
871 pa_log_debug("row #%d: { %s }", i, buf);
876 #ifdef WITH_RESOURCES
877 static void resource_attribute_destroy(resource_interface *rif,
878 resource_attribute *attr)
882 PA_LLIST_REMOVE(resource_attribute, rif->attrs, attr);
884 pa_xfree((void *)attr->prop);
885 pa_xfree((void *)attr->def.name);
887 if (attr->def.type == mqi_string)
888 pa_xfree((void *)attr->def.value.string);
894 static int resource_transport_connect(resource_interface *rif)
903 if (!mrp_transport_connect(rif->transp, &rif->saddr, rif->alen))
904 status = DISCONNECTED;
906 pa_log_info("resource transport connected to '%s'", rif->addr);
907 rif->connected = TRUE;
915 static void resource_xport_closed_evt(mrp_transport_t *transp, int error,
918 struct userdata *u = (struct userdata *)void_u;
919 pa_murphyif *murphyif;
920 resource_interface *rif;
925 pa_assert_se((murphyif = u->murphyif));
927 rif = &murphyif->resource;
930 pa_log("Resource transport connection closed by peer");
932 pa_log("Resource transport connection closed with error %d (%s)",
933 error, strerror(error));
936 resource_transport_destroy(murphyif);
937 resource_set_destroy_all(u);
938 schedule_connect(u, rif);
941 static mrp_msg_t *resource_create_request(uint32_t seqno,
942 mrp_resproto_request_t req)
947 msg = mrp_msg_create(RESPROTO_SEQUENCE_NO , MRP_MSG_FIELD_UINT32, seqno,
948 RESPROTO_REQUEST_TYPE, MRP_MSG_FIELD_UINT16, type ,
949 RESPROTO_MESSAGE_END );
952 pa_log("can't to create new resource message");
957 static pa_bool_t resource_send_message(resource_interface *rif,
963 resource_request *req;
964 pa_bool_t success = TRUE;
966 if (!mrp_transport_send(rif->transp, msg)) {
967 pa_log("failed to send resource message");
971 req = pa_xnew0(resource_request, 1);
972 req->nodidx = nodidx;
976 PA_LLIST_PREPEND(resource_request, rif->reqs, req);
984 static pa_bool_t resource_set_create_node(struct userdata *u,
986 pa_nodeset_resdef *resdef,
990 pa_murphyif *murphyif;
991 resource_interface *rif;
992 resource_request *req;
1001 pa_sink_input *sinp;
1002 pa_source_output *sout;
1003 audio_resource_t *res;
1005 mir_node_type type = 0;
1006 uint32_t audio_flags = 0;
1008 pa_proplist *proplist = NULL;
1009 pa_bool_t success = TRUE;
1013 pa_assert(node->index != PA_IDXSET_INVALID);
1014 pa_assert((!node->loop && node->implement == mir_stream) ||
1015 ( node->loop && node->implement == mir_device) );
1016 pa_assert(node->direction == mir_input || node->direction == mir_output);
1017 pa_assert(node->zone);
1018 pa_assert(!node->rsetid);
1020 pa_assert_se((core = u->core));
1022 if ((loop = node->loop)) {
1023 if (node->direction == mir_input) {
1024 sout = pa_idxset_get_by_index(core->source_outputs,
1025 loop->source_output_index);
1027 proplist = sout->proplist;
1030 sinp = pa_idxset_get_by_index(core->sink_inputs,
1031 loop->sink_input_index);
1033 proplist = sinp->proplist;
1035 if (proplist && (role = pa_proplist_gets(proplist, PA_PROP_MEDIA_ROLE))) {
1036 if ((map = pa_nodeset_get_map_by_role(u, role)))
1041 if (node->direction == mir_output) {
1042 if ((sout = pa_idxset_get_by_index(core->source_outputs, node->paidx)))
1043 proplist = sout->proplist;
1046 if ((sinp = pa_idxset_get_by_index(core->sink_inputs, node->paidx)))
1047 proplist = sinp->proplist;
1052 pa_assert_se((class = pa_nodeset_get_class(u, type)));
1053 pa_assert_se((murphyif = u->murphyif));
1054 rif = &murphyif->resource;
1056 reqid = RESPROTO_CREATE_RESOURCE_SET;
1057 seqno = rif->seqno.request++;
1058 res = (node->direction == mir_input) ? &rif->inpres : &rif->outres;
1060 pa_assert_se((resnam = res->name));
1062 rset_flags = RESPROTO_RSETFLAG_NOEVENTS;
1063 rset_flags |= (acquire ? RESPROTO_RSETFLAG_AUTOACQUIRE : 0);
1064 rset_flags |= (resdef ? resdef->flags.rset : 0);
1066 audio_flags = (resdef ? resdef->flags.audio : 0);
1068 priority = (resdef ? resdef->priority : 0);
1070 msg = resource_create_request(seqno, reqid);
1072 if (PUSH_VALUE(msg, RESOURCE_FLAGS , UINT32, rset_flags) &&
1073 PUSH_VALUE(msg, RESOURCE_PRIORITY, UINT32, priority) &&
1074 PUSH_VALUE(msg, CLASS_NAME , STRING, class) &&
1075 PUSH_VALUE(msg, ZONE_NAME , STRING, node->zone) &&
1076 PUSH_VALUE(msg, RESOURCE_NAME , STRING, resnam) &&
1077 PUSH_VALUE(msg, RESOURCE_FLAGS , UINT32, audio_flags) &&
1078 PUSH_VALUE(msg, ATTRIBUTE_NAME , STRING, "policy") &&
1079 PUSH_VALUE(msg, ATTRIBUTE_VALUE , STRING, "strict") &&
1080 PUSH_ATTRS(msg, rif, proplist) &&
1081 PUSH_VALUE(msg, SECTION_END , UINT8 , 0) )
1083 success = resource_send_message(rif, msg, node->index, reqid, seqno);
1091 pa_log_debug("requested resource set for '%s'", node->amname);
1093 pa_log_debug("failed to create resource set for '%s'", node->amname);
1098 static pa_bool_t resource_set_create_all(struct userdata *u)
1108 idx = PA_IDXSET_INVALID;
1110 while ((node = pa_nodeset_iterate_nodes(u, &idx))) {
1111 if ((node->implement == mir_stream && !node->loop) ||
1112 (node->implement == mir_device && node->loop) )
1114 if (!node->rsetid) {
1115 node->localrset = resource_set_create_node(u, node, NULL, FALSE);
1116 success &= node->localrset;
1124 static pa_bool_t resource_set_destroy_node(struct userdata *u, uint32_t rsetid)
1126 pa_murphyif *murphyif;
1127 resource_interface *rif;
1136 pa_assert_se((murphyif = u->murphyif));
1137 rif = &murphyif->resource;
1139 reqid = RESPROTO_DESTROY_RESOURCE_SET;
1140 seqno = rif->seqno.request++;
1141 nodidx = PA_IDXSET_INVALID;
1142 msg = resource_create_request(seqno, reqid);
1144 if (PUSH_VALUE(msg, RESOURCE_SET_ID, UINT32, rsetid))
1145 success = resource_send_message(rif, msg, nodidx, reqid, seqno);
1154 static pa_bool_t resource_set_destroy_all(struct userdata *u)
1156 pa_murphyif *murphyif;
1157 resource_interface *rif;
1165 pa_assert_se((murphyif = u->murphyif));
1167 rif = &murphyif->resource;
1171 idx = PA_IDXSET_INVALID;
1173 while ((node = pa_nodeset_iterate_nodes(u, &idx))) {
1174 if (node->implement == mir_stream && node->localrset) {
1175 pa_log_debug("destroying resource set for '%s'", node->amname);
1177 if (rif->connected && node->rsetid) {
1178 rsetid = strtoul(node->rsetid, &e, 10);
1180 if (e == node->rsetid || *e)
1183 rset_hashmap_remove(u, node->rsetid, node);
1184 success &= resource_set_destroy_node(u, rsetid);
1188 pa_xfree(node->rsetid);
1190 node->localrset = FALSE;
1191 node->rsetid = NULL;
1198 static void resource_set_notification(struct userdata *u,
1201 mrp_domctl_value_t **values)
1203 pa_murphyif *murphyif;
1204 resource_interface *rif;
1206 mrp_domctl_value_t *row;
1207 mrp_domctl_value_t *crsetid;
1208 mrp_domctl_value_t *cautorel;
1209 mrp_domctl_value_t *cstate;
1210 mrp_domctl_value_t *cgrant;
1211 mrp_domctl_value_t *cpid;
1212 mrp_domctl_value_t *cpolicy;
1215 mir_node *node, **nodes;
1217 rset_data rset, *rs;
1223 pa_assert_se((murphyif = u->murphyif));
1224 rif = &murphyif->resource;
1226 for (r = 0; r < nrow; r++) {
1228 crsetid = row + RESCOL_RSETID;
1229 cautorel = row + RESCOL_AUTOREL;
1230 cstate = row + RESCOL_STATE;
1231 cgrant = row + RESCOL_GRANT;
1232 cpid = row + RESCOL_PID;
1233 cpolicy = row + RESCOL_POLICY;
1235 if (crsetid->type != MRP_DOMCTL_UNSIGNED ||
1236 cautorel->type != MRP_DOMCTL_INTEGER ||
1237 cstate->type != MRP_DOMCTL_INTEGER ||
1238 cgrant->type != MRP_DOMCTL_INTEGER ||
1239 cpid->type != MRP_DOMCTL_STRING ||
1240 cpolicy->type != MRP_DOMCTL_STRING )
1242 pa_log("invalid field type in '%s' (%d|%d|%d|%d|%d|%d)", table,
1243 crsetid->type, cautorel->type, cstate->type,
1244 cgrant->type, cpid->type, cpolicy->type);
1248 snprintf(rsetid, sizeof(rsetid), "%d", crsetid->s32);
1252 rset.autorel = cautorel->s32;
1253 rset.state = cstate->s32;
1254 rset.grant = cgrant->s32;
1255 rset.policy = cpolicy->str;
1258 if (rset.autorel != 0 && rset.autorel != 1) {
1259 pa_log_debug("invalid autorel %d in table '%s'",
1260 rset.autorel, table);
1263 if (rset.state != RSET_RELEASE && rset.state != RSET_ACQUIRE) {
1264 pa_log_debug("invalid state %d in table '%s'", rset.state, table);
1267 if (rset.grant != 0 && rset.grant != 1) {
1268 pa_log_debug("invalid grant %d in table '%s'", rset.grant, table);
1272 if (!(rh = rset_hashmap_get(u, rset.id))) {
1274 pa_log_debug("can't find node for resource set %s "
1275 "(pid in resource set unknown)", rset.id);
1279 if ((node = pid_hashmap_remove_node(u, pid))) {
1280 pa_log_debug("found node %s for resource-set '%s'",
1281 node->amname, rset.id);
1283 if (!(rh = node_put_rset(u, node, &rset))) {
1284 pa_log("can't register resource set for node '%s': "
1285 "failed to set rsetid", node->amname);
1290 if (pid_hashmap_put(u, pid, NULL, rset_data_dup(&rset)) < 0) {
1291 if (!(rs = pid_hashmap_get_rset(u, pid)))
1292 pa_log("failed to add resource set to pid hash");
1294 if (!pa_streq(rs->id, rset.id)) {
1295 pa_log("process %s appears to have multiple resour"
1296 "ce sets (%s and %s)", pid, rs->id,rset.id);
1298 pa_log_debug("update resource-set %s data in "
1299 "pid hash (pid %s)", rs->id, pid);
1300 rset_data_copy(rs, &rset);
1304 pa_log_debug("can't find node for resource set %s. "
1305 "Beleive the stream will appear later on",
1313 rset_data_update(rh->rset, &rset);
1315 /* we need to make a copy of this as node_enforce_resource_policy()
1316 will delete/modify it */
1317 size = sizeof(mir_node *) * (rh->nnode + 1);
1318 nodes = alloca(size);
1319 memcpy(nodes, rh->nodes, size);
1321 for (i = 0; (node = nodes[i]); i++) {
1322 pa_log_debug("%u: resource notification for node '%s' autorel:%s "
1323 "state:%s grant:%s pid:%s policy:%s", i,
1324 node->amname, rset.autorel ? "yes":"no",
1325 rset.state == RSET_ACQUIRE ? "acquire":"release",
1326 rset.grant ? "yes":"no", pid, rset.policy);
1328 node_enforce_resource_policy(u, node, &rset);
1334 static pa_bool_t resource_push_attributes(mrp_msg_t *msg,
1335 resource_interface *rif,
1336 pa_proplist *proplist)
1338 resource_attribute *attr;
1352 PA_LLIST_FOREACH(attr, rif->attrs) {
1353 if (!PUSH_VALUE(msg, ATTRIBUTE_NAME, STRING, attr->def.name))
1357 sts = pa_proplist_get(proplist, attr->prop, &v.ptr, &size);
1361 switch (attr->def.type) {
1364 v.str = attr->def.value.string;
1365 else if (v.str[size-1] != '\0' || strlen(v.str) != (size-1) ||
1366 !pa_utf8_valid(v.str))
1368 if (!PUSH_VALUE(msg, ATTRIBUTE_VALUE, STRING, v.str))
1374 v.i32 = &attr->def.value.integer;
1375 else if (size != sizeof(*v.i32))
1377 if (!PUSH_VALUE(msg, ATTRIBUTE_VALUE, SINT8, *v.i32))
1383 v.u32 = &attr->def.value.unsignd;
1384 else if (size != sizeof(*v.u32))
1386 if (!PUSH_VALUE(msg, ATTRIBUTE_VALUE, SINT8, *v.u32))
1392 v.dbl = &attr->def.value.floating;
1393 else if (size != sizeof(*v.dbl))
1395 if (!PUSH_VALUE(msg, ATTRIBUTE_VALUE, SINT8, *v.dbl))
1399 default: /* we should never get here */
1409 static void resource_recv_msg(mrp_transport_t *t, mrp_msg_t *msg, void *void_u)
1411 return resource_recvfrom_msg(t, msg, NULL, 0, void_u);
1414 static void resource_recvfrom_msg(mrp_transport_t *transp, mrp_msg_t *msg,
1415 mrp_sockaddr_t *addr, socklen_t addrlen,
1418 struct userdata *u = (struct userdata *)void_u;
1420 pa_murphyif *murphyif;
1421 resource_interface *rif;
1426 resource_request *req, *n;
1431 MRP_UNUSED(addrlen);
1434 pa_assert_se((core = u->core));
1435 pa_assert_se((murphyif = u->murphyif));
1437 rif = &murphyif->resource;
1439 if (!resource_fetch_seqno (msg, &curs, &seqno) ||
1440 !resource_fetch_request (msg, &curs, &reqid) )
1442 pa_log("ignoring malformed message");
1446 PA_LLIST_FOREACH_SAFE(req, n, rif->reqs) {
1447 if (req->seqno <= seqno) {
1448 nodidx = req->nodidx;
1450 if (req->reqid == reqid) {
1451 PA_LLIST_REMOVE(resource_request, rif->reqs, req);
1455 if (!(node = mir_node_find_by_index(u, nodidx))) {
1456 if (reqid != RESPROTO_DESTROY_RESOURCE_SET) {
1457 pa_log("got response (reqid:%u seqno:%u) but can't "
1458 "find the corresponding node", reqid, seqno);
1459 resource_set_create_response_abort(u, msg, &curs);
1463 if (req->seqno < seqno) {
1464 pa_log("unanswered request %d", req->seqno);
1467 pa_log_debug("got response (reqid:%u seqno:%u "
1468 "node:'%s')", reqid, seqno,
1469 node ? node->amname : "<unknown>");
1472 case RESPROTO_CREATE_RESOURCE_SET:
1473 resource_set_create_response(u, node, msg, &curs);
1475 case RESPROTO_DESTROY_RESOURCE_SET:
1478 pa_log("ignoring unsupported resource request "
1484 } /* PA_LLIST_FOREACH_SAFE */
1489 static void resource_set_create_response(struct userdata *u, mir_node *node,
1490 mrp_msg_t *msg, void **pcursor)
1501 if (!resource_fetch_status(msg, pcursor, &status) || (status == 0 &&
1502 !resource_fetch_rset_id(msg, pcursor, &rsetid)))
1504 pa_log("ignoring malformed response to resource set creation");
1509 pa_log("creation of resource set failed. error code %u", status);
1513 node->rsetid = pa_sprintf_malloc("%d", rsetid);
1515 if (pa_murphyif_add_node(u, node) == 0) {
1516 pa_log_debug("resource set was successfully created");
1517 mir_node_print(node, buf, sizeof(buf));
1518 pa_log_debug("modified node:\n%s", buf);
1521 pa_log("failed to create resource set: "
1522 "conflicting resource set id");
1526 static void resource_set_create_response_abort(struct userdata *u,
1527 mrp_msg_t *msg, void **pcursor)
1536 if (!resource_fetch_status(msg, pcursor, &status) || (status == 0 &&
1537 !resource_fetch_rset_id(msg, pcursor, &rsetid)))
1539 pa_log("ignoring malformed response to resource set creation");
1544 pa_log("creation of resource set failed. error code %u", status);
1548 if (resource_set_destroy_node(u, rsetid))
1549 pa_log_debug("destroying resource set %u", rsetid);
1551 pa_log("attempt to destroy resource set %u failed", rsetid);
1555 static pa_bool_t resource_fetch_seqno(mrp_msg_t *msg,
1561 mrp_msg_value_t value;
1564 if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1565 tag != RESPROTO_SEQUENCE_NO || type != MRP_MSG_FIELD_UINT32)
1567 *pseqno = INVALID_SEQNO;
1571 *pseqno = value.u32;
1576 static pa_bool_t resource_fetch_request(mrp_msg_t *msg,
1582 mrp_msg_value_t value;
1585 if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1586 tag != RESPROTO_REQUEST_TYPE || type != MRP_MSG_FIELD_UINT16)
1588 *preqtype = INVALID_REQUEST;
1592 *preqtype = value.u16;
1596 static pa_bool_t resource_fetch_status(mrp_msg_t *msg,
1602 mrp_msg_value_t value;
1605 if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1606 tag != RESPROTO_REQUEST_STATUS || type != MRP_MSG_FIELD_SINT16)
1612 *pstatus = value.s16;
1616 static pa_bool_t resource_fetch_rset_id(mrp_msg_t *msg,
1622 mrp_msg_value_t value;
1625 if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1626 tag != RESPROTO_RESOURCE_SET_ID || type != MRP_MSG_FIELD_UINT32)
1636 static pa_bool_t resource_fetch_rset_state(mrp_msg_t *msg,
1638 mrp_resproto_state_t *pstate)
1642 mrp_msg_value_t value;
1645 if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1646 tag != RESPROTO_RESOURCE_STATE || type != MRP_MSG_FIELD_UINT16)
1652 *pstate = value.u16;
1657 static pa_bool_t resource_fetch_rset_mask(mrp_msg_t *msg,
1659 mrp_resproto_state_t *pmask)
1663 mrp_msg_value_t value;
1666 if (!mrp_msg_iterate(msg, pcursor, &tag, &type, &value, &size) ||
1667 tag != RESPROTO_RESOURCE_GRANT || type != MRP_MSG_FIELD_UINT32)
1677 static pa_bool_t resource_transport_create(struct userdata *u,
1678 pa_murphyif *murphyif)
1680 static mrp_transport_evt_t ev = {
1681 { .recvmsg = resource_recv_msg },
1682 { .recvmsgfrom = resource_recvfrom_msg },
1683 .closed = resource_xport_closed_evt,
1687 resource_interface *rif;
1690 pa_assert(murphyif);
1692 rif = &murphyif->resource;
1695 rif->transp = mrp_transport_create(murphyif->ml, rif->atype, &ev, u,0);
1697 return rif->transp ? TRUE : FALSE;
1700 static void resource_transport_destroy(pa_murphyif *murphyif)
1702 resource_interface *rif;
1704 pa_assert(murphyif);
1705 rif = &murphyif->resource;
1708 mrp_transport_destroy(rif->transp);
1711 rif->connected = FALSE;
1714 static void connect_attempt(pa_mainloop_api *a,
1716 const struct timeval *t,
1719 struct userdata *u = (struct userdata *)data;
1720 pa_murphyif *murphyif;
1721 resource_interface *rif;
1726 pa_assert_se((murphyif = u->murphyif));
1728 rif = &murphyif->resource;
1730 if (!resource_transport_create(u, murphyif))
1731 schedule_connect(u, rif);
1733 state = resource_transport_connect(rif);
1738 resource_set_create_all(u);
1739 cancel_schedule(u, rif);
1743 cancel_schedule(u, rif);
1747 schedule_connect(u, rif);
1753 static void schedule_connect(struct userdata *u, resource_interface *rif)
1756 pa_mainloop_api *mainloop;
1757 struct timeval when;
1762 pa_assert_se((core = u->core));
1763 pa_assert_se((mainloop = core->mainloop));
1765 pa_gettimeofday(&when);
1766 pa_timeval_add(&when, rif->connect.period);
1768 if ((tev = rif->connect.evt))
1769 mainloop->time_restart(tev, &when);
1771 rif->connect.evt = mainloop->time_new(mainloop, &when,
1772 connect_attempt, u);
1776 static void cancel_schedule(struct userdata *u, resource_interface *rif)
1779 pa_mainloop_api *mainloop;
1784 pa_assert_se((core = u->core));
1785 pa_assert_se((mainloop = core->mainloop));
1787 if ((tev = rif->connect.evt)) {
1788 mainloop->time_free(tev);
1789 rif->connect.evt = NULL;
1793 static rset_hash *node_put_rset(struct userdata *u, mir_node *node, rset_data *rset)
1795 pa_murphyif *murphyif;
1796 resource_interface *rif;
1803 pa_assert(rset->id);
1805 pa_assert(node->implement == mir_stream);
1806 pa_assert(node->direction == mir_input || node->direction == mir_output);
1808 pa_assert_se((murphyif = u->murphyif));
1809 rif = &murphyif->resource;
1811 pa_log_debug("setting rsetid %s for node %s", rset->id, node->amname);
1814 pa_xfree(node->rsetid);
1816 node->rsetid = pa_xstrdup(rset->id);
1818 if (!(pl = get_node_proplist(u, node))) {
1819 pa_log("can't obtain property list for node %s", node->amname);
1823 if ((pa_proplist_sets(pl, PA_PROP_RESOURCE_SET_ID, node->rsetid) < 0)) {
1824 pa_log("failed to set '" PA_PROP_RESOURCE_SET_ID "' property "
1825 "of '%s' node", node->amname);
1829 if (!(rh = rset_hashmap_put(u, node->rsetid, node))) {
1830 pa_log("conflicting rsetid %s for %s", node->rsetid, node->amname);
1837 static void node_enforce_resource_policy(struct userdata *u,
1845 pa_assert(rset->policy);
1848 if (pa_streq(rset->policy, "relaxed"))
1849 req = PA_STREAM_RUN;
1850 else if (pa_streq(rset->policy, "strict")) {
1851 if (rset->state == RSET_RELEASE)
1852 req = PA_STREAM_KILL;
1855 req = PA_STREAM_RUN;
1857 req = PA_STREAM_BLOCK;
1861 req = PA_STREAM_BLOCK;
1864 pa_stream_state_change(u, node, req);
1867 static rset_data *rset_data_dup(rset_data *orig)
1872 pa_assert(orig->id);
1873 pa_assert(orig->policy);
1875 dup = pa_xnew0(rset_data, 1);
1877 dup->id = pa_xstrdup(orig->id);
1878 dup->autorel = orig->autorel;
1879 dup->state = orig->state;
1880 dup->grant = orig->grant;
1881 dup->policy = pa_xstrdup(orig->policy);
1886 static void rset_data_copy(rset_data *dst, rset_data *src)
1893 pa_assert(src->policy);
1895 pa_xfree((void *)dst->id);
1896 pa_xfree((void *)dst->policy);
1898 dst->id = pa_xstrdup(src->id);
1899 dst->autorel = src->autorel;
1900 dst->state = src->state;
1901 dst->grant = src->grant;
1902 dst->policy = pa_xstrdup(src->policy);
1906 static void rset_data_update(rset_data *dst, rset_data *src)
1914 pa_assert(src->policy);
1916 pa_assert_se(pa_streq(src->id, dst->id));
1918 pa_xfree((void *)dst->policy);
1920 dst->autorel = src->autorel;
1921 dst->state = src->state;
1922 dst->grant = src->grant;
1923 dst->policy = pa_xstrdup(src->policy);
1927 static void rset_data_free(rset_data *rset)
1930 pa_xfree((void *)rset->id);
1931 pa_xfree((void *)rset->policy);
1936 static void pid_hashmap_free(void *p, void *userdata)
1938 pid_hash *ph = (pid_hash *)p;
1943 pa_xfree((void *)ph->pid);
1944 rset_data_free(ph->rset);
1949 static int pid_hashmap_put(struct userdata *u, const char *pid,
1950 mir_node *node, rset_data *rset)
1952 pa_murphyif *murphyif;
1953 resource_interface *rif;
1958 pa_assert(node || rset);
1959 pa_assert_se((murphyif = u->murphyif));
1961 rif = &murphyif->resource;
1963 ph = pa_xnew0(pid_hash, 1);
1964 ph->pid = pa_xstrdup(pid);
1968 if (pa_hashmap_put(rif->nodes.pid, ph->pid, ph) == 0)
1971 pid_hashmap_free(ph, NULL);
1976 static mir_node *pid_hashmap_get_node(struct userdata *u, const char *pid)
1978 pa_murphyif *murphyif;
1979 resource_interface *rif;
1984 pa_assert(murphyif = u->murphyif);
1986 rif = &murphyif->resource;
1988 if ((ph = pa_hashmap_get(rif->nodes.pid, pid)))
1994 static rset_data *pid_hashmap_get_rset(struct userdata *u, const char *pid)
1996 pa_murphyif *murphyif;
1997 resource_interface *rif;
2002 pa_assert(murphyif = u->murphyif);
2004 rif = &murphyif->resource;
2006 if ((ph = pa_hashmap_get(rif->nodes.pid, pid)))
2012 static mir_node *pid_hashmap_remove_node(struct userdata *u, const char *pid)
2014 pa_murphyif *murphyif;
2015 resource_interface *rif;
2020 pa_assert_se((murphyif = u->murphyif));
2022 rif = &murphyif->resource;
2024 if (!(ph = pa_hashmap_remove(rif->nodes.pid, pid)))
2026 else if (!(node = ph->node))
2027 pa_hashmap_put(rif->nodes.pid, ph->pid, ph);
2029 pid_hashmap_free(ph, NULL);
2034 static rset_data *pid_hashmap_remove_rset(struct userdata *u, const char *pid)
2036 pa_murphyif *murphyif;
2037 resource_interface *rif;
2044 pa_assert_se((murphyif = u->murphyif));
2046 rif = &murphyif->resource;
2048 if (!(ph = pa_hashmap_remove(rif->nodes.pid, pid)))
2050 else if (!(rset = ph->rset))
2051 pa_hashmap_put(rif->nodes.pid, ph->pid, ph);
2054 pid_hashmap_free(ph, NULL);
2061 static void rset_hashmap_free(void *r, void *userdata)
2063 rset_hash *rh = (rset_hash *)r;
2068 pa_xfree(rh->nodes);
2069 rset_data_free(rh->rset);
2074 static rset_hash *rset_hashmap_put(struct userdata *u,
2078 pa_murphyif *murphyif;
2079 resource_interface *rif;
2087 pa_assert_se((murphyif = u->murphyif));
2089 rif = &murphyif->resource;
2091 if ((rh = pa_hashmap_get(rif->nodes.rsetid, rsetid))) {
2092 for (i = 0; i < rh->nnode; i++) {
2093 if (rh->nodes[i] == node)
2098 rh->nodes = pa_xrealloc(rh->nodes, sizeof(mir_node *) * (rh->nnode+1));
2101 rset = pa_xnew0(rset_data, 1);
2103 rset->id = pa_xstrdup(rsetid);
2104 rset->policy = pa_xstrdup("unknown");
2106 rh = pa_xnew0(rset_hash, 1);
2109 rh->nodes = pa_xnew0(mir_node *, 2);
2112 pa_hashmap_put(rif->nodes.rsetid, rh->rset->id, rh);
2118 rh->nodes[i+0] = node;
2119 rh->nodes[i+1] = NULL;
2121 pa_log(" depth: %u", rh->nnode);
2126 static rset_hash *rset_hashmap_get(struct userdata *u, const char *rsetid)
2128 pa_murphyif *murphyif;
2129 resource_interface *rif;
2134 pa_assert(murphyif = u->murphyif);
2136 rif = &murphyif->resource;
2138 if ((rh = pa_hashmap_get(rif->nodes.rsetid, rsetid)))
2144 static int rset_hashmap_remove(struct userdata *u,
2148 pa_murphyif *murphyif;
2149 resource_interface *rif;
2154 pa_assert_se((murphyif = u->murphyif));
2156 rif = &murphyif->resource;
2158 if ((rh = pa_hashmap_get(rif->nodes.rsetid, rsetid))) {
2160 pa_log(" depth: %u", rh->nnode);
2162 for (i = 0; i < rh->nnode; i++) {
2163 if (node == rh->nodes[i]) {
2164 if (rh->nnode <= 1) {
2165 pa_hashmap_remove(rif->nodes.rsetid, rsetid);
2166 rset_hashmap_free(rh, NULL);
2170 for (j = i; j < rh->nnode; j++)
2171 rh->nodes[j] = rh->nodes[j+1];
2186 static pa_proplist *get_node_proplist(struct userdata *u, mir_node *node)
2190 pa_source_output *o;
2194 pa_assert_se((core = u->core));
2196 if (node->implement == mir_stream && node->paidx != PA_IDXSET_INVALID) {
2197 if (node->direction == mir_input) {
2198 if ((i = pa_idxset_get_by_index(core->sink_inputs, node->paidx)))
2201 else if (node->direction == mir_output) {
2202 if ((o = pa_idxset_get_by_index(core->source_outputs,node->paidx)))
2210 static const char *get_node_pid(struct userdata *u, mir_node *node)
2216 if (node && (pl = get_node_proplist(u, node)))
2217 return pa_proplist_gets(pl, PA_PROP_APPLICATION_PROCESS_ID);
2225 * indent-tabs-mode: nil